Merge branch 'feature/JAL-2527' into documentation/JAL-2675_release2102b1
authorJim Procter <jprocter@issues.jalview.org>
Sun, 3 Sep 2017 11:26:58 +0000 (12:26 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Sun, 3 Sep 2017 11:26:58 +0000 (12:26 +0100)
28 files changed:
.ant-targets-build.xml [new file with mode: 0644]
help/html/features/overview.html
help/html/releases.html
help/html/whatsNew.html
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/api/AlignmentColsCollectionI.java
src/jalview/api/AlignmentRowsCollectionI.java
src/jalview/appletgui/OverviewCanvas.java
src/jalview/appletgui/SequenceRenderer.java
src/jalview/datamodel/AllColsCollection.java
src/jalview/datamodel/AllRowsCollection.java
src/jalview/datamodel/VisibleColsCollection.java
src/jalview/datamodel/VisibleRowsCollection.java
src/jalview/gui/OverviewCanvas.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/Preferences.java
src/jalview/gui/SequenceRenderer.java
src/jalview/jbgui/GPreferences.java
src/jalview/renderer/OverviewRenderer.java
src/jalview/renderer/OverviewResColourFinder.java [new file with mode: 0644]
src/jalview/renderer/ResidueColourFinder.java [new file with mode: 0644]
src/jalview/schemes/ColourSchemeI.java
src/jalview/schemes/ResidueColourScheme.java
src/jalview/schemes/UserColourScheme.java
test/jalview/gui/SequenceRendererTest.java [deleted file]
test/jalview/renderer/OverviewResColourFinderTest.java [new file with mode: 0644]
test/jalview/renderer/ResidueColourFinderTest.java [new file with mode: 0644]

diff --git a/.ant-targets-build.xml b/.ant-targets-build.xml
new file mode 100644 (file)
index 0000000..15432a1
--- /dev/null
@@ -0,0 +1,31 @@
+build
+buildPropertiesFile
+buildTests
+buildextclients
+buildindices
+castorbinding
+clean
+compileApplet
+distclean
+help
+init
+linkcheck
+makeApplet
+makedist
+makefulldist
+obfuscate
+packageApplet
+prepare
+prepareTests
+preparejnlp
+prepubapplet_1
+pubapplet
+runenv
+signApplet
+sourcedist
+sourcedoc
+sourcescrub
+testclean
+testng
+usage
+writejnlpf
index ac5aecb..2a656a3 100755 (executable)
     <strong>View&#8594;Overview window</strong>
   </p>
   <p>Select the overview window menu item to get a navigable image
-    of the whole alignment.</p>
+    of the whole alignment. By default, gaps are shown as dark grey, in
+    the overview (since 2.10.2b1).</p>
   <p>The red box indicates the currently viewed region of the
     alignment, this may be moved by clicking and dragging with the
     mouse.</p>
   <p>Click anywhere else in the overview to centre the view on that
     position</p>
-    <p></p>
+  <p></p>
   <p>
     <strong>Hiding hidden regions in the overview</strong>
   </p>
     Hidden sequences and columns are by default shown as dark-grey rows
     and columns in the overview. Hidden regions can also be omitted
     entirely (to make the Overview 'WYSIWIG') by <strong>Right-clicking</strong>
-    (or CMD-Clicking) to open the overview's popup menu.<br />
-    <br /> <em>The option to include/exclude hidden regions in the
+    (or CMD-Clicking) to open the overview's popup menu.<br /> <br />
+    <em>The option to include/exclude hidden regions in the
       overview was introduced in Jalview 2.10.2</em>.
   
   <p>
     <img src="overview.gif" width="407" height="137">
   </p>
   <p>&nbsp;</p>
+  <em>Previous to 2.10.2b1, gaps were white, and sequences shown as
+    dark grey unless coloured.</em>
 </body>
 </html>
index 3ff32f9..9cae363 100755 (executable)
@@ -77,6 +77,11 @@ li:before {
       <td><div align="left">
           <em></em>
           <ul>
+            <li>
+              <!-- JAL-2588 -->Show gaps in overview window by colouring
+              in grey (sequences used to be coloured grey, and gaps were
+              white)
+            </li>
           </ul>
         </div></td>
       <td><div align="left">
@@ -86,6 +91,13 @@ li:before {
               <!-- JAL-2681 -->duplicate protein sequences shown after
               retreiving Ensembl crossrefs for sequences from Uniprot
             </li>
+            <li>  <!-- JAL-2603 -->Overview window throws NPE if show boxes
+              format setting is unticked
+            </li>
+            <li>
+              <!-- JAL-2610 -->Groups are coloured wrongly in overview
+              if group has show boxes format setting unticked
+            </li>
           </ul>
         </div></td>
     
index c002eb8..169e7fe 100755 (executable)
@@ -30,6 +30,9 @@
     This is patch release for 2.10.2. See the <a
       href="releases.html#Jalview.2.10.2b1">release notes</a>.
   </p>
+  <ul>
+    <li>Gaps are now rendered as dark grey in overview window</li>
+  </ul>
   <p>
     <strong>What's new in Jalview 2.10.2 ?</strong>
   </p>
index 162f10f..28fbfd5 100644 (file)
@@ -1311,3 +1311,12 @@ label.occupancy_descr = Number of aligned positions
 label.show_experimental = Enable experimental features
 label.show_experimental_tip = Enable any new and currently 'experimental' features (see Latest Release Notes for details)
 label.warning_hidden = Warning: {0} {1} is currently hidden
+label.overview_settings = Overview settings
+label.ov_legacy_gap = Use legacy gap colouring (gaps are white)
+label.gap_colour = Gap colour:
+label.ov_show_hide_default = Show hidden regions when opening overview
+label.hidden_colour = Hidden colour:
+label.select_gap_colour = Select gap colour
+label.select_hidden_colour = Select hidden colour
+label.overview = Overview
+label.reset_to_defaults = Reset to defaults
\ No newline at end of file
index 8385142..b5b5704 100644 (file)
@@ -1311,3 +1311,12 @@ label.togglehidden = Mostrar regiones ocultas
 label.show_experimental = Habilitar funciones experimentales
 label.show_experimental_tip = Habilitar funciones nuevas y experimentales (ver Latest Release Notes para más detalles)
 label.warning_hidden = Advertencia: {0} {1} está actualmente oculto
+label.overview_settings = Ajustes para la ventana resumen
+label.ov_legacy_gap = <html>Utilizar el color heredado de huecos<br>(los huecos son blancos)
+label.gap_colour = Color de huecos:
+label.ov_show_hide_default = Mostrar regiones ocultas al abrir el resumen
+label.hidden_colour = Color de las regiones ocultas:
+label.select_gap_colour = Seleccionar color de huecos
+label.select_hidden_colour = Seleccionar color de las regiones ocultas 
+label.overview = Resumen
+label.reset_to_defaults = Restablecen a los predeterminados
\ No newline at end of file
index 3a3f660..06b1675 100644 (file)
@@ -30,4 +30,11 @@ public interface AlignmentColsCollectionI extends Iterable<Integer>
    * @return true if the column at the position is hidden
    */
   public boolean isHidden(int c);
+
+  /**
+   * Answers if any column in this collection is hidden
+   * 
+   * @return true if there is at least 1 hidden column
+   */
+  public boolean hasHidden();
 }
index 24affc5..fbf6ceb 100644 (file)
@@ -34,6 +34,13 @@ public interface AlignmentRowsCollectionI extends Iterable<Integer>
   public boolean isHidden(int r);
 
   /**
+   * Answers if any row in this collection is hidden
+   * 
+   * @return true if there is at least 1 hidden row
+   */
+  public boolean hasHidden();
+
+  /**
    * Answers the sequence at the given position in the alignment
    * 
    * @param r
index a0466d3..9597b44 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.appletgui;
 
 import jalview.renderer.OverviewRenderer;
+import jalview.renderer.OverviewResColourFinder;
 import jalview.viewmodel.OverviewDimensions;
 
 import java.awt.Color;
@@ -48,10 +49,6 @@ public class OverviewCanvas extends Component
 
   private AlignViewport av;
 
-  // Can set different properties in this seqCanvas than
-  // main visible SeqCanvas
-  private SequenceRenderer sr;
-
   private jalview.renderer.seqfeatures.FeatureRenderer fr;
 
   private Frame nullFrame;
@@ -65,10 +62,6 @@ public class OverviewCanvas extends Component
     nullFrame = new Frame();
     nullFrame.addNotify();
 
-    sr = new SequenceRenderer(av);
-    sr.graphics = nullFrame.getGraphics();
-    sr.renderGaps = false;
-    sr.forOverview = true;
     fr = new jalview.renderer.seqfeatures.FeatureRenderer(av);
   }
 
@@ -119,7 +112,8 @@ public class OverviewCanvas extends Component
 
     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
 
-    or = new OverviewRenderer(sr, fr, od);
+    or = new OverviewRenderer(fr, od, av.getAlignment(),
+            av.getResidueShading(), new OverviewResColourFinder());
     miniMe = nullFrame.createImage(od.getWidth(), od.getHeight());
     offscreen = nullFrame.createImage(od.getWidth(), od.getHeight());
 
index a382c60..e55f2a5 100755 (executable)
@@ -22,7 +22,7 @@ package jalview.appletgui;
 
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-import jalview.renderer.ResidueShaderI;
+import jalview.renderer.ResidueColourFinder;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
 
 import java.awt.Color;
@@ -40,19 +40,18 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
   boolean renderGaps = true;
 
-  SequenceGroup currentSequenceGroup = null;
-
   SequenceGroup[] allGroups = null;
 
   Color resBoxColour;
 
   Graphics graphics;
 
-  boolean forOverview = false;
+  ResidueColourFinder resColourFinder;
 
   public SequenceRenderer(AlignViewport av)
   {
     this.av = av;
+    resColourFinder = new ResidueColourFinder();
   }
 
   /**
@@ -69,25 +68,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     this.renderGaps = renderGaps;
   }
 
-  protected Color getResidueBoxColour(SequenceI seq, int i)
-  {
-    allGroups = av.getAlignment().findAllGroups(seq);
-
-    if (inCurrentSequenceGroup(i))
-    {
-      if (currentSequenceGroup.getDisplayBoxes())
-      {
-        getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i);
-      }
-    }
-    else if (av.getShowBoxes())
-    {
-      getBoxColour(av.getResidueShading(), seq, i);
-    }
-
-    return resBoxColour;
-  }
-
   /**
    * Get the residue colour at the given sequence position - as determined by
    * the sequence group colour (if any), else the colour scheme, possibly
@@ -104,31 +84,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   {
     // TODO replace 8 or so code duplications with calls to this method
     // (refactored as needed)
-    Color col = getResidueBoxColour(seq, position);
-
-    if (finder != null)
-    {
-      col = finder.findFeatureColour(col, seq, position);
-    }
-    return col;
-  }
-
-  void getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
-  {
-    if (shader.getColourScheme() != null)
-    {
-      resBoxColour = shader.findColour(seq.getCharAt(i), i, seq);
-    }
-    else if (forOverview
-            && !jalview.util.Comparison.isGap(seq.getCharAt(i)))
-    {
-      resBoxColour = Color.lightGray;
-    }
-    else
-    {
-      resBoxColour = Color.white;
-    }
-
+    return resColourFinder.getResidueColour(av.getShowBoxes(),
+            av.getResidueShading(),
+            allGroups, seq, position, finder);
   }
 
   public Color findSequenceColour(SequenceI seq, int i)
@@ -165,23 +123,28 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     int curWidth = av.getCharWidth(), avCharWidth = av.getCharWidth(),
             avCharHeight = av.getCharHeight();
 
+    Color resBoxColour = Color.white;
     Color tempColour = null;
     while (i <= end)
     {
       resBoxColour = Color.white;
       if (i < length)
       {
-        if (inCurrentSequenceGroup(i))
+        SequenceGroup currentSequenceGroup = resColourFinder
+                .getCurrentSequenceGroup(allGroups, i);
+        if (currentSequenceGroup != null)
         {
           if (currentSequenceGroup.getDisplayBoxes())
           {
-            getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq,
+            resBoxColour = resColourFinder.getBoxColour(
+                    currentSequenceGroup.getGroupColourScheme(), seq,
                     i);
           }
         }
         else if (av.getShowBoxes())
         {
-          getBoxColour(av.getResidueShading(), seq, i);
+          resBoxColour = resColourFinder
+                  .getBoxColour(av.getResidueShading(), seq, i);
         }
       }
 
@@ -245,7 +208,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
         continue;
       }
 
-      if (inCurrentSequenceGroup(i))
+      SequenceGroup currentSequenceGroup = resColourFinder
+              .getCurrentSequenceGroup(allGroups, i);
+      if (currentSequenceGroup != null)
       {
         if (!currentSequenceGroup.getDisplayText())
         {
@@ -254,7 +219,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
         if (currentSequenceGroup.getColourText())
         {
-          getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i);
+          resBoxColour = resColourFinder.getBoxColour(
+                  currentSequenceGroup.getGroupColourScheme(), seq, i);
           graphics.setColor(resBoxColour.darker());
         }
         if (currentSequenceGroup.getShowNonconserved())
@@ -271,7 +237,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
         if (av.getColourText())
         {
-          getBoxColour(av.getResidueShading(), seq, i);
+          resBoxColour = resColourFinder
+                  .getBoxColour(av.getResidueShading(), seq, i);
           if (av.getShowBoxes())
           {
             graphics.setColor(resBoxColour.darker());
@@ -358,28 +325,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     return sequenceChar;
   }
 
-  boolean inCurrentSequenceGroup(int res)
-  {
-    if (allGroups == null)
-    {
-      return false;
-    }
-
-    for (int i = 0; i < allGroups.length; i++)
-    {
-      if (allGroups[i].getStartRes() <= res
-              && allGroups[i].getEndRes() >= res)
-      {
-        currentSequenceGroup = allGroups[i];
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-  public void drawHighlightedText(SequenceI seq, int start, int end, int x1,
-          int y1)
+  public void drawHighlightedText(SequenceI seq, int start, int end,
+          int x1, int y1)
   {
     int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
     int pady = avCharHeight / 5;
index 4ac4f83..e216c46 100644 (file)
@@ -50,4 +50,10 @@ public class AllColsCollection implements AlignmentColsCollectionI
   {
     return !hidden.isVisible(c);
   }
+
+  @Override
+  public boolean hasHidden()
+  {
+    return hidden.hasHiddenColumns();
+  }
 }
index 9a33094..10c47f0 100644 (file)
@@ -59,4 +59,10 @@ public class AllRowsCollection implements AlignmentRowsCollectionI
   {
     return alignment.getSequenceAtAbsoluteIndex(seq);
   }
+
+  @Override
+  public boolean hasHidden()
+  {
+    return (hidden.getSize() > 0);
+  }
 }
index bc32fac..e9437a7 100644 (file)
@@ -51,4 +51,10 @@ public class VisibleColsCollection implements AlignmentColsCollectionI
     return false;
   }
 
+  @Override
+  public boolean hasHidden()
+  {
+    return false;
+  }
+
 }
index ee0557f..fd7cf47 100644 (file)
@@ -56,4 +56,10 @@ public class VisibleRowsCollection implements AlignmentRowsCollectionI
   {
     return alignment.getSequenceAtAbsoluteIndex(seq);
   }
+
+  @Override
+  public boolean hasHidden()
+  {
+    return false;
+  }
 }
index 27f9c3f..ed1497a 100644 (file)
@@ -21,7 +21,9 @@
 package jalview.gui;
 
 import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
 import jalview.renderer.OverviewRenderer;
+import jalview.renderer.OverviewResColourFinder;
 import jalview.viewmodel.OverviewDimensions;
 
 import java.awt.Color;
@@ -57,6 +59,8 @@ public class OverviewCanvas extends JComponent
 
   private AlignViewportI av;
 
+  private OverviewResColourFinder cf;
+
   public OverviewCanvas(OverviewDimensions overviewDims,
           AlignViewportI alignvp)
   {
@@ -65,8 +69,14 @@ public class OverviewCanvas extends JComponent
 
     sr = new SequenceRenderer(av);
     sr.renderGaps = false;
-    sr.forOverview = true;
     fr = new jalview.renderer.seqfeatures.FeatureRenderer(av);
+
+    boolean useLegacy = Cache.getDefault(Preferences.USE_LEGACY_GAP, false);
+    Color gapCol = Cache.getDefaultColour(Preferences.GAP_COLOUR,
+            Preferences.OVERVIEW_DEFAULT_GAP);
+    Color hiddenCol = Cache.getDefaultColour(Preferences.HIDDEN_COLOUR,
+            Preferences.OVERVIEW_DEFAULT_HIDDEN);
+    cf = new OverviewResColourFinder(useLegacy, gapCol, hiddenCol);
   }
 
   /**
@@ -126,7 +136,8 @@ public class OverviewCanvas extends JComponent
 
     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
 
-    or = new OverviewRenderer(sr, fr, od);
+    or = new OverviewRenderer(fr, od, av.getAlignment(),
+            av.getResidueShading(), cf);
     miniMe = or.draw(od.getRows(av.getAlignment()),
             od.getColumns(av.getAlignment()));
 
index 28de801..3c94f26 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+import jalview.bin.Cache;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.viewmodel.OverviewDimensions;
@@ -79,9 +80,20 @@ public class OverviewPanel extends JPanel
     this.av = alPanel.av;
     this.ap = alPanel;
 
-    od = new OverviewDimensionsShowHidden(av.getRanges(),
+    showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START,
+            true);
+    if (showHidden)
+    {
+      od = new OverviewDimensionsShowHidden(av.getRanges(),
             (av.isShowAnnotation()
                     && av.getAlignmentConservationAnnotation() != null));
+    }
+    else
+    {
+      od = new OverviewDimensionsHideHidden(av.getRanges(),
+              (av.isShowAnnotation()
+                      && av.getAlignmentConservationAnnotation() != null));
+    }
 
     setSize(od.getWidth(), od.getHeight());
 
@@ -158,7 +170,6 @@ public class OverviewPanel extends JPanel
           }
         }
         else
-        // if (!av.getWrapAlignment())
         {
           if (!od.isPositionInBox(evt.getX(), evt.getY()))
           {
index c3c9239..7ceb04e 100755 (executable)
@@ -109,10 +109,25 @@ public class Preferences extends GPreferences
 
   public static final String SHOW_OCCUPANCY = "SHOW_OCCUPANCY";
 
+  public static final String SHOW_OV_HIDDEN_AT_START = "SHOW_OV_HIDDEN_AT_START";
+
+  public static final String USE_LEGACY_GAP = "USE_LEGACY_GAP";
+
+  public static final String GAP_COLOUR = "GAP_COLOUR";
+
+  public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR";
+
   private static final int MIN_FONT_SIZE = 1;
 
   private static final int MAX_FONT_SIZE = 30;
 
+  public static final Color OVERVIEW_DEFAULT_GAP = Color.lightGray;
+
+  public static final Color OVERVIEW_DEFAULT_LEGACY_GAP = Color.white;
+
+  public static final Color OVERVIEW_DEFAULT_HIDDEN = Color.darkGray
+          .darker();
+
   /**
    * Holds name and link separated with | character. Sequence ID must be
    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
@@ -155,7 +170,7 @@ public class Preferences extends GPreferences
      * .properties file as '|' separated strings
      */
 
-    groupURLLinks = new ArrayList<String>();
+    groupURLLinks = new ArrayList<>();
   }
 
   JInternalFrame frame;
@@ -309,6 +324,20 @@ public class Preferences extends GPreferences
             Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red));
 
     /*
+     * Set overview panel defaults
+     */
+    gapColour.setBackground(
+            Cache.getDefaultColour(GAP_COLOUR, OVERVIEW_DEFAULT_GAP));
+    hiddenColour.setBackground(
+            Cache.getDefaultColour(HIDDEN_COLOUR,
+                    OVERVIEW_DEFAULT_HIDDEN));
+    useLegacyGap.setSelected(Cache.getDefault(USE_LEGACY_GAP, false));
+    gapLabel.setEnabled(!useLegacyGap.isSelected());
+    gapColour.setEnabled(!useLegacyGap.isSelected());
+    showHiddenAtStart
+            .setSelected(Cache.getDefault(SHOW_OV_HIDDEN_AT_START, true));
+
+    /*
      * Set Structure tab defaults.
      */
     final boolean structSelected = Cache.getDefault(STRUCT_FROM_PDB, false);
@@ -631,6 +660,16 @@ public class Preferences extends GPreferences
             maxColour.getBackground());
 
     /*
+     * Save Overview settings
+     */
+    Cache.setColourProperty(GAP_COLOUR, gapColour.getBackground());
+    Cache.setColourProperty(HIDDEN_COLOUR, hiddenColour.getBackground());
+    Cache.applicationProperties.setProperty(USE_LEGACY_GAP,
+            Boolean.toString(useLegacyGap.isSelected()));
+    Cache.applicationProperties.setProperty(SHOW_OV_HIDDEN_AT_START,
+            Boolean.toString(showHiddenAtStart.isSelected()));
+
+    /*
      * Save Structure settings
      */
     Cache.applicationProperties.setProperty(ADD_TEMPFACT_ANN,
@@ -1035,6 +1074,60 @@ public class Preferences extends GPreferences
   }
 
   @Override
+  public void gapColour_actionPerformed(JPanel gap)
+  {
+    if (!useLegacyGap.isSelected())
+    {
+      Color col = JColorChooser.showDialog(this,
+              MessageManager.getString("label.select_gap_colour"),
+              gapColour.getBackground());
+      if (col != null)
+      {
+        gap.setBackground(col);
+      }
+      gap.repaint();
+    }
+  }
+
+  @Override
+  public void hiddenColour_actionPerformed(JPanel hidden)
+  {
+    Color col = JColorChooser.showDialog(this,
+            MessageManager.getString("label.select_hidden_colour"),
+            hiddenColour.getBackground());
+    if (col != null)
+    {
+      hidden.setBackground(col);
+    }
+    hidden.repaint();
+  }
+
+  @Override
+  protected void useLegacyGaps_actionPerformed(ActionEvent e)
+  {
+    boolean enabled = useLegacyGap.isSelected();
+    if (enabled)
+    {
+      gapColour.setBackground(OVERVIEW_DEFAULT_LEGACY_GAP);
+    }
+    else
+    {
+      gapColour.setBackground(OVERVIEW_DEFAULT_GAP);
+    }
+    gapColour.setEnabled(!enabled);
+    gapLabel.setEnabled(!enabled);
+  }
+
+  @Override
+  protected void resetOvDefaults_actionPerformed(ActionEvent e)
+  {
+    useLegacyGap.setSelected(false);
+    useLegacyGaps_actionPerformed(null);
+    showHiddenAtStart.setSelected(true);
+    hiddenColour.setBackground(OVERVIEW_DEFAULT_HIDDEN);
+  }
+
+  @Override
   protected void userIdWidth_actionPerformed()
   {
     try
index 4498f88..5be7f55 100755 (executable)
@@ -23,9 +23,8 @@ package jalview.gui;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-import jalview.renderer.ResidueShaderI;
+import jalview.renderer.ResidueColourFinder;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
-import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.awt.FontMetrics;
@@ -41,17 +40,15 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
   boolean renderGaps = true;
 
-  SequenceGroup currentSequenceGroup = null;
-
   SequenceGroup[] allGroups = null;
 
-  Color resBoxColour;
+  // Color resBoxColour;
 
   Graphics graphics;
 
   boolean monospacedFont;
 
-  boolean forOverview = false;
+  ResidueColourFinder resColourFinder;
 
   /**
    * Creates a new SequenceRenderer object
@@ -61,6 +58,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   public SequenceRenderer(AlignViewportI viewport)
   {
     this.av = viewport;
+    resColourFinder = new ResidueColourFinder();
   }
 
   /**
@@ -83,26 +81,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     this.renderGaps = renderGaps;
   }
 
-  protected Color getResidueBoxColour(SequenceI seq, int i)
-  {
-    // rate limiting step when rendering overview for lots of groups
-    allGroups = av.getAlignment().findAllGroups(seq);
-
-    if (inCurrentSequenceGroup(i))
-    {
-      if (currentSequenceGroup.getDisplayBoxes())
-      {
-        getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i);
-      }
-    }
-    else if (av.getShowBoxes())
-    {
-      getBoxColour(av.getResidueShading(), seq, i);
-    }
-
-    return resBoxColour;
-  }
-
   /**
    * Get the residue colour at the given sequence position - as determined by
    * the sequence group colour (if any), else the colour scheme, possibly
@@ -117,39 +95,10 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   public Color getResidueColour(final SequenceI seq, int position,
           FeatureColourFinder finder)
   {
-    Color col = getResidueBoxColour(seq, position);
-
-    if (finder != null)
-    {
-      col = finder.findFeatureColour(col, seq, position);
-    }
-    return col;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param shader
-   *          DOCUMENT ME!
-   * @param seq
-   *          DOCUMENT ME!
-   * @param i
-   *          DOCUMENT ME!
-   */
-  void getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
-  {
-    if (shader.getColourScheme() != null)
-    {
-      resBoxColour = shader.findColour(seq.getCharAt(i), i, seq);
-    }
-    else if (forOverview && !Comparison.isGap(seq.getCharAt(i)))
-    {
-      resBoxColour = Color.lightGray;
-    }
-    else
-    {
-      resBoxColour = Color.white;
-    }
+    return resColourFinder.getResidueColour(av.getShowBoxes(),
+            av.getResidueShading(),
+            allGroups, seq, position,
+            finder);
   }
 
   /**
@@ -208,6 +157,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   public synchronized void drawBoxes(SequenceI seq, int start, int end,
           int y1)
   {
+    Color resBoxColour = Color.white;
+
     if (seq == null)
     {
       return; // fix for racecondition
@@ -227,17 +178,22 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
       if (i < length)
       {
-        if (inCurrentSequenceGroup(i))
+        SequenceGroup currentSequenceGroup = resColourFinder
+                .getCurrentSequenceGroup(
+                allGroups, i);
+        if (currentSequenceGroup != null)
         {
           if (currentSequenceGroup.getDisplayBoxes())
           {
-            getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq,
+            resBoxColour = resColourFinder.getBoxColour(
+                    currentSequenceGroup.getGroupColourScheme(), seq,
                     i);
           }
         }
         else if (av.getShowBoxes())
         {
-          getBoxColour(av.getResidueShading(), seq, i);
+          resBoxColour = resColourFinder
+                  .getBoxColour(av.getResidueShading(), seq, i);
         }
       }
 
@@ -317,10 +273,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
       boolean srep = av.isDisplayReferenceSeq();
       boolean getboxColour = false;
       boolean isarep = av.getAlignment().getSeqrep() == seq;
-      boolean isgrep = currentSequenceGroup != null
-              ? currentSequenceGroup.getSeqrep() == seq
-              : false;
-      char sr_c;
+      Color resBoxColour = Color.white;
+
       for (int i = start; i <= end; i++)
       {
 
@@ -333,7 +287,10 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           continue;
         }
 
-        if (inCurrentSequenceGroup(i))
+        SequenceGroup currentSequenceGroup = resColourFinder
+                .getCurrentSequenceGroup(
+                allGroups, i);
+        if (currentSequenceGroup != null)
         {
           if (!currentSequenceGroup.getDisplayText())
           {
@@ -344,7 +301,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
                   || currentSequenceGroup.getColourText())
           {
             getboxColour = true;
-            getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq,
+            resBoxColour = resColourFinder.getBoxColour(
+                    currentSequenceGroup.getGroupColourScheme(), seq,
                     i);
 
             if (currentSequenceGroup.getColourText())
@@ -366,6 +324,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           {
             graphics.setColor(currentSequenceGroup.textColour);
           }
+          boolean isgrep = currentSequenceGroup != null
+                  ? currentSequenceGroup.getSeqrep() == seq : false;
           if (!isarep && !isgrep
                   && currentSequenceGroup.getShowNonconserved()) // todo
                                                                  // optimize
@@ -386,7 +346,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           if (av.getColourText())
           {
             getboxColour = true;
-            getBoxColour(av.getResidueShading(), seq, i);
+            resBoxColour = resColourFinder
+                    .getBoxColour(av.getResidueShading(), seq, i);
 
             if (av.getShowBoxes())
             {
@@ -402,7 +363,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           {
             if (!getboxColour)
             {
-              getBoxColour(av.getResidueShading(), seq, i);
+              resBoxColour = resColourFinder
+                      .getBoxColour(av.getResidueShading(), seq, i);
             }
 
             if (resBoxColour.getRed() + resBoxColour.getBlue()
@@ -473,35 +435,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   /**
    * DOCUMENT ME!
    * 
-   * @param res
-   *          DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  boolean inCurrentSequenceGroup(int res)
-  {
-    if (allGroups == null)
-    {
-      return false;
-    }
-
-    for (int i = 0; i < allGroups.length; i++)
-    {
-      if ((allGroups[i].getStartRes() <= res)
-              && (allGroups[i].getEndRes() >= res))
-      {
-        currentSequenceGroup = allGroups[i];
-
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
    * @param seq
    *          DOCUMENT ME!
    * @param start
index 633d2b8..1ca0802 100755 (executable)
@@ -95,11 +95,11 @@ public class GPreferences extends JPanel
 
   protected JCheckBox rightAlign = new JCheckBox();
 
-  protected JComboBox<String> fontSizeCB = new JComboBox<String>();
+  protected JComboBox<String> fontSizeCB = new JComboBox<>();
 
-  protected JComboBox<String> fontStyleCB = new JComboBox<String>();
+  protected JComboBox<String> fontStyleCB = new JComboBox<>();
 
-  protected JComboBox<String> fontNameCB = new JComboBox<String>();
+  protected JComboBox<String> fontNameCB = new JComboBox<>();
 
   protected JCheckBox showOccupancy = new JCheckBox();
 
@@ -111,15 +111,15 @@ public class GPreferences extends JPanel
 
   protected JCheckBox scaleProteinToCdna = new JCheckBox();
 
-  protected JComboBox<String> gapSymbolCB = new JComboBox<String>();
+  protected JComboBox<String> gapSymbolCB = new JComboBox<>();
 
   protected JCheckBox wrap = new JCheckBox();
 
-  protected JComboBox<String> sortby = new JComboBox<String>();
+  protected JComboBox<String> sortby = new JComboBox<>();
 
-  protected JComboBox<String> sortAnnBy = new JComboBox<String>();
+  protected JComboBox<String> sortAnnBy = new JComboBox<>();
 
-  protected JComboBox<String> sortAutocalc = new JComboBox<String>();
+  protected JComboBox<String> sortAutocalc = new JComboBox<>();
 
   protected JCheckBox startupCheckbox = new JCheckBox();
 
@@ -159,7 +159,7 @@ public class GPreferences extends JPanel
 
   protected JCheckBox addTempFactor = new JCheckBox();
 
-  protected JComboBox<String> structViewer = new JComboBox<String>();
+  protected JComboBox<String> structViewer = new JComboBox<>();
 
   protected JTextField chimeraPath = new JTextField();
 
@@ -176,9 +176,22 @@ public class GPreferences extends JPanel
 
   protected JPanel maxColour = new JPanel();
 
-  protected JComboBox<String> protColour = new JComboBox<String>();
+  protected JComboBox<String> protColour = new JComboBox<>();
 
-  protected JComboBox<String> nucColour = new JComboBox<String>();
+  protected JComboBox<String> nucColour = new JComboBox<>();
+
+  /*
+   * Overview tab components
+   */
+  protected JPanel gapColour = new JPanel();
+
+  protected JPanel hiddenColour = new JPanel();
+
+  protected JCheckBox useLegacyGap;
+
+  protected JCheckBox showHiddenAtStart;
+
+  protected JLabel gapLabel;
 
   /*
    * Connections tab components
@@ -216,7 +229,7 @@ public class GPreferences extends JPanel
   /*
    * Output tab components
    */
-  protected JComboBox<Object> epsRendering = new JComboBox<Object>();
+  protected JComboBox<Object> epsRendering = new JComboBox<>();
 
   protected JLabel userIdWidthlabel = new JLabel();
 
@@ -294,6 +307,9 @@ public class GPreferences extends JPanel
     tabbedPane.add(initColoursTab(),
             MessageManager.getString("label.colours"));
 
+    tabbedPane.add(initOverviewTab(),
+            MessageManager.getString("label.overview"));
+
     tabbedPane.add(initStructureTab(),
             MessageManager.getString("label.structure"));
 
@@ -963,6 +979,151 @@ public class GPreferences extends JPanel
   }
 
   /**
+   * Initialises the Overview tabbed panel.
+   * 
+   * @return
+   */
+  private JPanel initOverviewTab()
+  {
+    JPanel overviewPanel = new JPanel();
+    overviewPanel.setBorder(new TitledBorder(
+            MessageManager.getString("label.overview_settings")));
+
+    gapColour.setFont(LABEL_FONT);
+    // fixing the border colours stops apparent colour bleed from the panel
+    gapColour.setBorder(
+            BorderFactory.createEtchedBorder(Color.white, Color.lightGray));
+    gapColour.setPreferredSize(new Dimension(40, 20));
+    gapColour.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mousePressed(MouseEvent e)
+      {
+        gapColour_actionPerformed(gapColour);
+      }
+    });
+
+    hiddenColour.setFont(LABEL_FONT);
+    // fixing the border colours stops apparent colour bleed from the panel
+    hiddenColour.setBorder(
+            BorderFactory.createEtchedBorder(Color.white, Color.lightGray));
+    hiddenColour.setPreferredSize(new Dimension(40, 20));
+    hiddenColour.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mousePressed(MouseEvent e)
+      {
+        hiddenColour_actionPerformed(hiddenColour);
+      }
+    });
+    
+    useLegacyGap = new JCheckBox(
+            MessageManager.getString("label.ov_legacy_gap"));
+    useLegacyGap.setFont(LABEL_FONT);
+    useLegacyGap.setHorizontalAlignment(SwingConstants.LEFT);
+    useLegacyGap.setVerticalTextPosition(SwingConstants.TOP);
+    gapLabel = new JLabel(
+            MessageManager.getString("label.gap_colour"));
+    gapLabel.setFont(LABEL_FONT);
+    gapLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    gapLabel.setVerticalTextPosition(SwingConstants.TOP);
+    showHiddenAtStart = new JCheckBox(
+            MessageManager.getString("label.ov_show_hide_default"));
+    showHiddenAtStart.setFont(LABEL_FONT);
+    showHiddenAtStart.setHorizontalAlignment(SwingConstants.LEFT);
+    showHiddenAtStart.setVerticalTextPosition(SwingConstants.TOP);
+    JLabel hiddenLabel = new JLabel(
+            MessageManager.getString("label.hidden_colour"));
+    hiddenLabel.setFont(LABEL_FONT);
+    hiddenLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    hiddenLabel.setVerticalTextPosition(SwingConstants.TOP);
+
+    useLegacyGap.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        useLegacyGaps_actionPerformed(e);
+      }
+    });
+
+    overviewPanel.setLayout(new GridBagLayout());
+    GridBagConstraints c1 = new GridBagConstraints();
+
+    c1.fill = GridBagConstraints.HORIZONTAL;
+    c1.gridx = 0;
+    c1.gridy = 0;
+    c1.weightx = 1;
+    c1.ipady = 20;
+    c1.anchor = GridBagConstraints.FIRST_LINE_START;
+    overviewPanel.add(useLegacyGap, c1);
+
+    GridBagConstraints c2 = new GridBagConstraints();
+    c2.fill = GridBagConstraints.HORIZONTAL;
+    c2.gridx = 1;
+    c2.gridy = 0;
+    c2.insets = new Insets(0, 15, 0, 10);
+    overviewPanel.add(gapLabel, c2);
+
+    GridBagConstraints c3 = new GridBagConstraints();
+    c3.fill = GridBagConstraints.HORIZONTAL;
+    c3.gridx = 2;
+    c3.gridy = 0;
+    c3.insets = new Insets(0, 0, 0, 15);
+    overviewPanel.add(gapColour, c3);
+
+    GridBagConstraints c4 = new GridBagConstraints();
+    c4.fill = GridBagConstraints.HORIZONTAL;
+    c4.gridx = 0;
+    c4.gridy = 1;
+    c4.weightx = 1;
+    overviewPanel.add(showHiddenAtStart, c4);
+
+    GridBagConstraints c5 = new GridBagConstraints();
+    c5.fill = GridBagConstraints.HORIZONTAL;
+    c5.gridx = 1;
+    c5.gridy = 1;
+    c5.insets = new Insets(0, 15, 0, 10);
+    overviewPanel.add(hiddenLabel, c5);
+
+    GridBagConstraints c6 = new GridBagConstraints();
+    c6.fill = GridBagConstraints.HORIZONTAL;
+    c6.gridx = 2;
+    c6.gridy = 1;
+    c6.insets = new Insets(0, 0, 0, 15);
+    overviewPanel.add(hiddenColour, c6);
+
+    JButton resetButton = new JButton(
+            MessageManager.getString("label.reset_to_defaults"));
+
+    resetButton.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        resetOvDefaults_actionPerformed(e);
+      }
+    });
+
+    GridBagConstraints c7 = new GridBagConstraints();
+    c7.fill = GridBagConstraints.NONE;
+    c7.gridx = 0;
+    c7.gridy = 2;
+    c7.insets = new Insets(10, 0, 0, 0);
+    c7.anchor = GridBagConstraints.WEST;
+    overviewPanel.add(resetButton, c7);
+
+    // Add padding so the panel doesn't look ridiculous
+    JPanel spacePanel = new JPanel();
+    overviewPanel.add(spacePanel,
+            new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.BOTH,
+                    new Insets(0, 0, 0, 5), 0, 0));
+
+    return overviewPanel;
+  }
+
+  /**
    * Initialises the Structure tabbed panel.
    * 
    * @return
@@ -1494,12 +1655,28 @@ public class GPreferences extends JPanel
   {
   }
 
+  protected void gapColour_actionPerformed(JPanel panel)
+  {
+  }
+
+  protected void hiddenColour_actionPerformed(JPanel panel)
+  {
+  }
+
   protected void showunconserved_actionPerformed(ActionEvent e)
   {
     // TODO Auto-generated method stub
 
   }
 
+  protected void useLegacyGaps_actionPerformed(ActionEvent e)
+  {
+  }
+
+  protected void resetOvDefaults_actionPerformed(ActionEvent e)
+  {
+  }
+
   /**
    * DOCUMENT ME!
    * 
index 0f8cda6..992a1b2 100644 (file)
@@ -23,21 +23,28 @@ package jalview.renderer;
 import jalview.api.AlignmentColsCollectionI;
 import jalview.api.AlignmentRowsCollectionI;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.renderer.seqfeatures.FeatureRenderer;
 import jalview.viewmodel.OverviewDimensions;
 
+import java.awt.AlphaComposite;
 import java.awt.Color;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 
 public class OverviewRenderer
 {
-  private FeatureColourFinder finder;
+  // transparency of hidden cols/seqs overlay
+  private final float TRANSPARENCY = 0.5f;
+
+  private final Color HIDDEN_COLOUR = Color.DARK_GRAY.darker();
 
-  private jalview.api.SequenceRenderer sr;
+  private FeatureColourFinder finder;
 
   // image to render on
   private BufferedImage miniMe;
@@ -51,11 +58,22 @@ public class OverviewRenderer
   // flag to indicate whether to halt drawing
   private volatile boolean redraw = false;
 
-  public OverviewRenderer(jalview.api.SequenceRenderer seqRenderer,
-          FeatureRenderer fr, OverviewDimensions od)
+  // reference to alignment, needed to get sequence groups
+  private AlignmentI al;
+
+  private ResidueShaderI shader;
+
+  private OverviewResColourFinder resColFinder;
+
+  public OverviewRenderer(FeatureRenderer fr, OverviewDimensions od,
+          AlignmentI alignment,
+          ResidueShaderI resshader, OverviewResColourFinder colFinder)
   {
-    sr = seqRenderer;
     finder = new FeatureColourFinder(fr);
+    resColFinder = colFinder;
+
+    al = alignment;
+    shader = resshader;
 
     pixelsPerCol = od.getPixelsPerCol();
     pixelsPerSeq = od.getPixelsPerSeq();
@@ -85,15 +103,17 @@ public class OverviewRenderer
       {
         break;
       }
-
+    
       // get details of this alignment row
-      boolean hidden = rows.isHidden(alignmentRow);
       SequenceI seq = rows.getSequence(alignmentRow);
 
+      // rate limiting step when rendering overview for lots of groups
+      SequenceGroup[] allGroups = al.findAllGroups(seq);
+
       // calculate where this row extends to in pixels
       int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
               miniMe.getHeight() - 1);
-
+    
       int colIndex = 0;
       int pixelCol = 0;
       for (int alignmentCol : cols)
@@ -102,21 +122,19 @@ public class OverviewRenderer
         {
           break;
         }
-
+    
         // calculate where this column extends to in pixels
         int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
                 miniMe.getWidth() - 1);
-
+    
         // don't do expensive colour determination if we're not going to use it
         // NB this is important to avoid performance issues in the overview
         // panel
         if (pixelCol <= endCol)
         {
-          // determine the colour based on the sequence and column position
-          rgbcolor = getColumnColourFromSequence(seq,
-                  hidden || cols.isHidden(alignmentCol), alignmentCol,
-                  finder);
-
+          rgbcolor = getColumnColourFromSequence(allGroups, seq,
+                  alignmentCol, finder);
+    
           // fill in the appropriate number of pixels
           for (int row = pixelRow; row <= endRow; ++row)
           {
@@ -125,7 +143,7 @@ public class OverviewRenderer
               miniMe.setRGB(col, row, rgbcolor);
             }
           }
-
+    
           pixelCol = endCol + 1;
         }
         colIndex++;
@@ -133,28 +151,146 @@ public class OverviewRenderer
       pixelRow = endRow + 1;
       seqIndex++;
     }
+
+    overlayHiddenRegions(rows, cols);
     return miniMe;
   }
 
-  /*
+  /**
    * Find the colour of a sequence at a specified column position
+   * 
+   * @param seq
+   *          sequence to get colour for
+   * @param lastcol
+   *          column position to get colour for
+   * @param fcfinder
+   *          FeatureColourFinder to use
+   * @return colour of sequence at this position, as RGB
    */
-  private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq,
-          boolean isHidden, int lastcol, FeatureColourFinder fcfinder)
+  private int getColumnColourFromSequence(SequenceGroup[] allGroups,
+          jalview.datamodel.SequenceI seq,
+          int lastcol, FeatureColourFinder fcfinder)
   {
     Color color = Color.white;
 
     if ((seq != null) && (seq.getLength() > lastcol))
     {
-      color = sr.getResidueColour(seq, lastcol, fcfinder);
+      color = resColFinder.getResidueColour(true, shader, allGroups, seq,
+              lastcol,
+              fcfinder);
     }
 
-    if (isHidden)
+    return color.getRGB();
+  }
+
+  /**
+   * Overlay the hidden regions on the overview image
+   * 
+   * @param rows
+   *          collection of rows the overview is built over
+   * @param cols
+   *          collection of columns the overview is built over
+   */
+  private void overlayHiddenRegions(AlignmentRowsCollectionI rows,
+          AlignmentColsCollectionI cols)
+  {
+    if (cols.hasHidden() || rows.hasHidden())
     {
-      color = color.darker().darker();
+      BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(),
+              miniMe.getHeight());
+
+      Graphics2D g = (Graphics2D) miniMe.getGraphics();
+      g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+              TRANSPARENCY));
+      g.drawImage(mask, 0, 0, miniMe.getWidth(), miniMe.getHeight(), null);
     }
+  }
 
-    return color.getRGB();
+  /**
+   * Build a masking image of hidden columns and rows to be applied on top of
+   * the main overview image.
+   * 
+   * @param rows
+   *          collection of rows the overview is built over
+   * @param cols
+   *          collection of columns the overview is built over
+   * @param width
+   *          width of overview in pixels
+   * @param height
+   *          height of overview in pixels
+   * @return BufferedImage containing mask of hidden regions
+   */
+  private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows,
+          AlignmentColsCollectionI cols, int width, int height)
+  {
+    // new masking image
+    BufferedImage hiddenImage = new BufferedImage(width, height,
+            BufferedImage.TYPE_INT_ARGB);
+
+    int colIndex = 0;
+    int pixelCol = 0;
+
+    Color hidden = resColFinder.getHiddenColour();
+
+    Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics();
+
+    // set background to transparent
+    g2d.setComposite(AlphaComposite.Clear);
+    g2d.fillRect(0, 0, width, height);
+
+    // set next colour to opaque
+    g2d.setComposite(AlphaComposite.Src);
+
+    for (int alignmentCol : cols)
+    {
+      if (redraw)
+      {
+        break;
+      }
+
+      // calculate where this column extends to in pixels
+      int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
+              hiddenImage.getWidth() - 1);
+
+      if (pixelCol <= endCol)
+      {
+        // determine the colour based on the sequence and column position
+        if (cols.isHidden(alignmentCol))
+        {
+          g2d.setColor(hidden);
+          g2d.fillRect(pixelCol, 0, endCol - pixelCol + 1, height);
+        }
+
+        pixelCol = endCol + 1;
+      }
+      colIndex++;
+
+    }
+
+    int seqIndex = 0;
+    int pixelRow = 0;
+    for (int alignmentRow : rows)
+    {
+      if (redraw)
+      {
+        break;
+      }
+
+      // calculate where this row extends to in pixels
+      int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
+              miniMe.getHeight() - 1);
+
+      // get details of this alignment row
+      if (rows.isHidden(alignmentRow))
+      {
+        g2d.setColor(hidden);
+        g2d.fillRect(0, pixelRow, width, endRow - pixelRow + 1);
+      }
+      pixelRow = endRow + 1;
+      seqIndex++;
+    }
+
+    return hiddenImage;
   }
 
   /**
@@ -222,6 +358,13 @@ public class OverviewRenderer
     }
   }
 
+  /**
+   * Allows redraw flag to be set
+   * 
+   * @param b
+   *          value to set redraw to: true = redraw is occurring, false = no
+   *          redraw
+   */
   public void setRedraw(boolean b)
   {
     synchronized (this)
diff --git a/src/jalview/renderer/OverviewResColourFinder.java b/src/jalview/renderer/OverviewResColourFinder.java
new file mode 100644 (file)
index 0000000..b606ba3
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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 
+ * 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.
+ * 
+ * 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.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.Preferences;
+import jalview.util.Comparison;
+
+import java.awt.Color;
+
+public class OverviewResColourFinder extends ResidueColourFinder
+{
+  final Color GAP_COLOUR; // default colour to use at gaps
+
+  final Color RESIDUE_COLOUR; // default colour to use at residues
+
+  final Color HIDDEN_COLOUR; // colour for hidden regions
+
+  boolean useLegacy = false;
+
+  /**
+   * Constructor without colour settings (used by applet)
+   */
+  public OverviewResColourFinder()
+  {
+    this(false, Preferences.OVERVIEW_DEFAULT_GAP,
+            Preferences.OVERVIEW_DEFAULT_HIDDEN);
+  }
+
+  /**
+   * Constructor with colour settings
+   * 
+   * @param useLegacyColouring
+   *          whether to use legacy gap colouring (white gaps, grey residues)
+   * @param gapCol
+   *          gap colour if not legacy
+   * @param hiddenCol
+   *          hidden region colour (transparency applied by rendering code)
+   */
+  public OverviewResColourFinder(boolean useLegacyColouring, Color gapCol,
+          Color hiddenCol)
+  {
+    if (useLegacyColouring)
+    {
+      GAP_COLOUR = Color.white;
+      RESIDUE_COLOUR = Color.lightGray;
+      HIDDEN_COLOUR = hiddenCol;
+    }
+    else
+    {
+      GAP_COLOUR = gapCol;
+      RESIDUE_COLOUR = Color.white;
+      HIDDEN_COLOUR = hiddenCol;
+    }
+  }
+
+  @Override
+  public Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
+  {
+    Color resBoxColour = RESIDUE_COLOUR;
+    char currentChar = seq.getCharAt(i);
+
+    // In the overview window, gaps are coloured grey, unless the colour scheme
+    // specifies a gap colour, in which case gaps honour the colour scheme
+    // settings
+    if (shader.getColourScheme() != null)
+    {
+      if (Comparison.isGap(currentChar)
+              && (!shader.getColourScheme().hasGapColour()))
+      {
+        resBoxColour = GAP_COLOUR;
+      }
+      else
+      {
+        resBoxColour = shader.findColour(currentChar, i, seq);
+      }
+    }
+    else if (Comparison.isGap(currentChar))
+    {
+      resBoxColour = GAP_COLOUR;
+    }
+
+    return resBoxColour;
+  }
+
+  /**
+   * {@inheritDoc} In the overview, the showBoxes setting is ignored, as the
+   * overview displays the colours regardless.
+   */
+  @Override
+  protected Color getResidueBoxColour(boolean showBoxes,
+          ResidueShaderI shader,
+          SequenceGroup[] allGroups, SequenceI seq, int i)
+  {
+    ResidueShaderI currentShader;
+    SequenceGroup currentSequenceGroup = getCurrentSequenceGroup(allGroups,
+            i);
+    if (currentSequenceGroup != null)
+    {
+      currentShader = currentSequenceGroup.getGroupColourScheme();
+    }
+    else
+    {
+      currentShader = shader;
+    }
+
+    return getBoxColour(currentShader, seq, i);
+  }
+
+  /**
+   * Supply hidden colour
+   * 
+   * @return colour of hidden regions
+   */
+  protected Color getHiddenColour()
+  {
+    return HIDDEN_COLOUR;
+  }
+}
diff --git a/src/jalview/renderer/ResidueColourFinder.java b/src/jalview/renderer/ResidueColourFinder.java
new file mode 100644 (file)
index 0000000..2da7233
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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 
+ * 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.
+ * 
+ * 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.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
+
+import java.awt.Color;
+
+public class ResidueColourFinder
+{
+  public ResidueColourFinder()
+  {
+  }
+
+  /**
+   * Get the colour of a residue in a sequence
+   * 
+   * @param showBoxes
+   *          true if the viewport's Show Boxes setting is true
+   * @param shader
+   *          the viewport's colour scheme
+   * @param allGroups
+   *          all the groups which seq participates in
+   * @param seq
+   *          the sequence containing the residue
+   * @param position
+   *          the position of the residue in the sequence
+   * @param finder
+   *          FeatureColourFinder for the viewport
+   * @return colour of the residue
+   */
+  public Color getResidueColour(boolean showBoxes, ResidueShaderI shader,
+          SequenceGroup[] allGroups,
+          final SequenceI seq, int position, FeatureColourFinder finder)
+  {
+    Color col = getResidueBoxColour(showBoxes, shader, allGroups, seq,
+            position);
+
+    // if there's a FeatureColourFinder we might override the residue colour
+    // here with feature colouring
+    if (finder != null)
+    {
+      col = finder.findFeatureColour(col, seq, position);
+    }
+    return col;
+  }
+
+  /**
+   * Get the residue colour without accounting for any features
+   * 
+   * @param showBoxes
+   *          true if the viewport's Show Boxes setting is true
+   * @param shader
+   *          the viewport's colour scheme
+   * @param allGroups
+   *          all the groups which seq participates in
+   * @param seq
+   *          the sequence containing the residue
+   * @param i
+   *          the position of the residue in the sequence
+   * @return
+   */
+  protected Color getResidueBoxColour(boolean showBoxes,
+          ResidueShaderI shader,
+          SequenceGroup[] allGroups,
+          SequenceI seq, int i)
+  {
+    SequenceGroup currentSequenceGroup = getCurrentSequenceGroup(allGroups,
+            i);
+    if (currentSequenceGroup != null)
+    {
+      if (currentSequenceGroup.getDisplayBoxes())
+      {
+        return getBoxColour(currentSequenceGroup.getGroupColourScheme(),
+                seq, i);
+      }
+    }
+    else if (showBoxes)
+    {
+      return getBoxColour(shader, seq, i);
+    }
+  
+    return Color.white;
+  }
+
+  /**
+   * Search all the groups for a sequence to find the one which a given res
+   * falls into
+   * 
+   * @param allGroups
+   *          all the groups a sequence participates in
+   * @param res
+   *          the residue to search for
+   * @return a sequence group for res, or null if no sequence group applies
+   */
+  public SequenceGroup getCurrentSequenceGroup(SequenceGroup[] allGroups,
+          int res)
+  {
+    if (allGroups == null)
+    {
+      return null;
+    }
+
+    for (int i = 0; i < allGroups.length; i++)
+    {
+      if ((allGroups[i].getStartRes() <= res)
+              && (allGroups[i].getEndRes() >= res))
+      {
+        return (allGroups[i]);
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param shader
+   *          the viewport's colour scheme
+   * @param seq
+   *          the sequence containing the residue
+   * @param i
+   *          the position of the residue in the sequence
+   */
+  public Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
+  {
+    Color resBoxColour = Color.white;
+    if (shader.getColourScheme() != null)
+    {
+      resBoxColour = shader.findColour(seq.getCharAt(i), i, seq);
+    }
+    return resBoxColour;
+  }
+
+}
index f16ca21..d70b4e2 100755 (executable)
@@ -98,4 +98,11 @@ public interface ColourSchemeI
    * @return
    */
   boolean isSimple();
+
+  /**
+   * Answers true if the colour scheme has a colour specified for gaps.
+   * 
+   * @return
+   */
+  boolean hasGapColour();
 }
index 34a5daa..2f7a5e0 100755 (executable)
@@ -208,4 +208,14 @@ public abstract class ResidueColourScheme implements ColourSchemeI
   {
     return true;
   }
+
+  /**
+   * Default method returns false. Override this to return true in colour
+   * schemes that have a colour associated with gap residues.
+   */
+  @Override
+  public boolean hasGapColour()
+  {
+    return false;
+  }
 }
index b86250a..bf62e45 100755 (executable)
@@ -287,7 +287,7 @@ public class UserColourScheme extends ResidueColourScheme
     /*
      * step 1: build a map from colours to the symbol(s) that have the colour
      */
-    Map<Color, List<String>> colours = new HashMap<Color, List<String>>();
+    Map<Color, List<String>> colours = new HashMap<>();
 
     for (char symbol = 'A'; symbol <= 'Z'; symbol++)
     {
@@ -320,7 +320,7 @@ public class UserColourScheme extends ResidueColourScheme
     /*
      * step 2: make a list of { A,G,R=12f9d6 } residues/colour specs
      */
-    List<String> residueColours = new ArrayList<String>();
+    List<String> residueColours = new ArrayList<>();
     for (Entry<Color, List<String>> cols : colours.entrySet())
     {
       boolean first = true;
@@ -350,4 +350,10 @@ public class UserColourScheme extends ResidueColourScheme
     Collections.sort(residueColours);
     return StringUtils.listToDelimitedString(residueColours, ";");
   }
+
+  @Override
+  public boolean hasGapColour()
+  {
+    return (findColour(' ') != null);
+  }
 }
diff --git a/test/jalview/gui/SequenceRendererTest.java b/test/jalview/gui/SequenceRendererTest.java
deleted file mode 100644 (file)
index c80b830..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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 
- * 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.
- * 
- * 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.gui;
-
-import static org.testng.AssertJUnit.assertEquals;
-
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.schemes.ZappoColourScheme;
-
-import java.awt.Color;
-
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-public class SequenceRendererTest
-{
-
-  @BeforeClass(alwaysRun = true)
-  public void setUpJvOptionPane()
-  {
-    JvOptionPane.setInteractiveMode(false);
-    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
-  }
-
-  @Test(groups = { "Functional" })
-  public void testGetResidueBoxColour_zappo()
-  {
-    SequenceI seq = new Sequence("name", "MATVLGSPRAPAFF"); // FER1_MAIZE...
-    AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
-    SequenceRenderer sr = new SequenceRenderer(av);
-    av.setGlobalColourScheme(new ZappoColourScheme());
-
-    // @see ResidueProperties.zappo
-    assertEquals(Color.pink, sr.getResidueBoxColour(seq, 0)); // M
-    assertEquals(Color.green, sr.getResidueBoxColour(seq, 2)); // T
-    assertEquals(Color.magenta, sr.getResidueBoxColour(seq, 5)); // G
-    assertEquals(Color.orange, sr.getResidueBoxColour(seq, 12)); // F
-  }
-
-  @Test(groups = { "Functional" })
-  public void testGetResidueBoxColour_none()
-  {
-    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
-    AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
-    SequenceRenderer sr = new SequenceRenderer(av);
-
-    assertEquals(Color.white, sr.getResidueBoxColour(seq, 0));
-    assertEquals(Color.white, sr.getResidueBoxColour(seq, 2));
-
-    // set for overview
-    sr.forOverview = true;
-    assertEquals(Color.lightGray, sr.getResidueBoxColour(seq, 0));
-    assertEquals(Color.white, sr.getResidueBoxColour(seq, 2));
-  }
-
-  // TODO more tests for getResidueBoxColour covering groups, feature rendering,
-  // gaps, overview...
-
-}
diff --git a/test/jalview/renderer/OverviewResColourFinderTest.java b/test/jalview/renderer/OverviewResColourFinderTest.java
new file mode 100644 (file)
index 0000000..2016087
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * 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 
+ * 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.
+ * 
+ * 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 static org.testng.AssertJUnit.assertEquals;
+
+import jalview.bin.Cache;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignViewport;
+import jalview.gui.JvOptionPane;
+import jalview.gui.Preferences;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.UserColourScheme;
+import jalview.schemes.ZappoColourScheme;
+
+import java.awt.Color;
+import java.util.ArrayList;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class OverviewResColourFinderTest
+{
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueBoxColour_none()
+  {
+    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+
+    // gaps are grey, residues white
+    assertEquals(Color.white, rcf.getResidueColour(true,
+            av.getResidueShading(),
+            null, seq, 0, null));
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 2,
+                    null));
+
+    // unaffected by showBoxes setting
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 0, null));
+    assertEquals(Color.lightGray, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 2, null));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueBoxColour_zappo()
+  {
+    SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
+                                                            // gap
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+    av.setGlobalColourScheme(new ZappoColourScheme());
+
+    // @see ResidueProperties.zappo
+    assertEquals(Color.pink,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 0, null)); // M
+    assertEquals(Color.green,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 2, null)); // T
+    assertEquals(Color.magenta,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 5, null)); // G
+    assertEquals(Color.orange,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 12, null)); // F
+
+    // gap colour not specified so gaps are lightGray
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 3,
+                    null));
+
+    // unaffected by showBoxes setting
+    assertEquals(Color.pink, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 0, null)); // M
+    assertEquals(Color.green, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 2, null)); // T
+    assertEquals(Color.magenta, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 5, null)); // G
+    assertEquals(Color.orange, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 12, null)); // F
+
+    // gap colour not specified so gaps are lightGray
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(false, av.getResidueShading(), null, seq, 3,
+                    null));
+
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueBoxColour_userdef()
+  {
+    SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
+                                                            // gap
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+
+    Color[] newColours = new Color[24];
+    for (int i = 0; i < 24; i++)
+    {
+      newColours[i] = null;
+    }
+
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour not specified so gaps are lightGray
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 3,
+                    null));
+
+    newColours[23] = Color.pink;
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour specified as pink
+    assertEquals(Color.pink, rcf.getResidueColour(true,
+            av.getResidueShading(),
+            null, seq, 3, null));
+
+    // unaffected by showBoxes setting
+    // gap colour not specified so gaps are lightGray
+    newColours[23] = null;
+    assertEquals(Color.lightGray, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 3, null));
+
+    newColours[23] = Color.pink;
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour specified as pink
+    assertEquals(Color.pink, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 3, null));
+  }
+
+  @Test
+  public void testGetResidueBoxColour_group()
+  {
+    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    
+    ColourSchemeI cs = new ZappoColourScheme();
+    ArrayList<SequenceI> seqlist = new ArrayList<>();
+    seqlist.add(seq);
+    SequenceGroup sg = new SequenceGroup(seqlist, "testgroup", cs, true,
+            true, true, 5, 9);
+    al.addGroup(sg);
+    SequenceGroup[] groups = new SequenceGroup[1];
+    groups[0] = sg;
+    
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+    
+    // G in group specified as magenta in Zappo
+    assertEquals(Color.magenta, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 7, null));
+
+    // Residue outside group coloured white
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 0, null));
+
+    // Gap outside group coloured lightgray
+    assertEquals(Color.lightGray, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 2, null));
+
+    // use legacy colouring
+    rcf = new OverviewResColourFinder(true, Color.blue, Color.red);
+  
+    // G in group specified as magenta in Zappo
+    assertEquals(Color.magenta, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 7, null));
+
+    // Residue outside group coloured lightgray
+    assertEquals(Color.lightGray, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 0, null));
+
+    // Gap outside group coloured white
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 2, null));
+
+    // use new colouring
+    rcf = new OverviewResColourFinder(false, Color.blue, Color.red);
+
+    // G in group specified as magenta in Zappo
+    assertEquals(Color.magenta, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 7, null));
+
+    // Residue outside group coloured white
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 0, null));
+
+    // Gap outside group coloured blue
+    assertEquals(Color.blue, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 2, null));
+  }
+
+  @Test
+  public void testGetBoxColour()
+  {
+    SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
+                                                            // gap
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+
+    // non-legacy colouring
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+    ResidueShaderI shader = new ResidueShader();
+
+    // residues white
+    Color c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.white, c);
+
+    // gaps gap colour
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(Preferences.OVERVIEW_DEFAULT_GAP, c);
+
+    // legacy colouring set explicitly via constructor
+    rcf = new OverviewResColourFinder(true, Color.blue, Color.red);
+    shader = new ResidueShader();
+
+    // residues light gray
+    c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.lightGray, c);
+
+    // gaps white
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(Color.white, c);
+
+    // legacy colouring off
+    rcf = new OverviewResColourFinder();
+    shader = new ResidueShader();
+
+    // residues white
+    c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.white, c);
+
+    // gaps gap colour
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(Preferences.OVERVIEW_DEFAULT_GAP, c);
+
+    // non legacy colouring with colour scheme
+    rcf = new OverviewResColourFinder(false, Color.blue, Color.red);
+    shader = new ResidueShader(new ZappoColourScheme());
+
+    // M residue pink
+    c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.pink, c);
+
+    // gaps blue
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(Color.blue, c);
+
+    // legacy colouring with colour scheme
+    rcf = new OverviewResColourFinder(true, Color.blue, Color.red);
+
+    // M residue pink
+    c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.pink, c);
+
+    // gaps white
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(Color.white, c);
+  }
+}
diff --git a/test/jalview/renderer/ResidueColourFinderTest.java b/test/jalview/renderer/ResidueColourFinderTest.java
new file mode 100644 (file)
index 0000000..81fb2c0
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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 
+ * 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.
+ * 
+ * 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 static org.testng.AssertJUnit.assertEquals;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignViewport;
+import jalview.gui.JvOptionPane;
+import jalview.schemes.UserColourScheme;
+import jalview.schemes.ZappoColourScheme;
+
+import java.awt.Color;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ResidueColourFinderTest
+{
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueColour_zappo()
+  {
+    SequenceI seq = new Sequence("name", "MATVLGSPRAPAFF"); // FER1_MAIZE...
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new ResidueColourFinder();
+    av.setGlobalColourScheme(new ZappoColourScheme());
+
+    // @see ResidueProperties.zappo
+    assertEquals(Color.pink,
+            rcf.getResidueColour(true, av.getResidueShading(), null, seq, 0,
+                    null)); // M
+    assertEquals(Color.green,
+            rcf.getResidueColour(true, av.getResidueShading(), null, seq, 2,
+                    null)); // T
+    assertEquals(Color.magenta,
+            rcf.getResidueColour(true, av.getResidueShading(), null, seq, 5,
+                    null)); // G
+    assertEquals(Color.orange,
+            rcf.getResidueColour(true, av.getResidueShading(), null, seq,
+                    12,
+                    null)); // F
+
+    // everything is white if showBoxes is false
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 0, null)); // M
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 2, null)); // T
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 5, null)); // G
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 12, null)); // F
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueColour_none()
+  {
+    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new ResidueColourFinder();
+
+    assertEquals(Color.white,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 0, null));
+    assertEquals(Color.white,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 2, null));
+
+    // no change if showBoxes is false
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 0, null));
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 2, null));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueColour_userdef()
+  {
+    SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
+                                                            // gap
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new ResidueColourFinder();
+
+    Color[] newColours = new Color[24];
+    for (int i = 0; i < 24; i++)
+    {
+      newColours[i] = null;
+    }
+
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour not specified so gap colour is null
+    // this is consistent with previous behaviour, but may not be correct?
+    assertEquals(null, rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 3, null));
+
+    newColours[23] = Color.pink;
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour specified as pink
+    assertEquals(Color.pink, rcf.getResidueColour(true,
+            av.getResidueShading(),
+            null, seq, 3, null));
+
+    // everything is white if showBoxes is false
+    newColours[23] = null;
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(),
+            null, seq, 3, null));
+
+    newColours[23] = Color.pink;
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour specified as pink
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 3, null));
+  }
+
+  // TODO more tests for getResidueColour covering groups, feature rendering...
+}