JAL-3390 unit tests and command and menu refinements
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 9 Jun 2020 10:19:33 +0000 (11:19 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 9 Jun 2020 10:19:33 +0000 (11:19 +0100)
18 files changed:
src/jalview/datamodel/ColumnSelection.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolCommands.java
src/jalview/ext/pymol/PymolCommands.java
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/ext/rbvi/chimera/ChimeraXCommands.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/PymolViewer.java
src/jalview/gui/StructureViewerBase.java
src/jalview/structure/StructureCommandsI.java
src/jalview/structures/models/AAStructureBindingModel.java
test/jalview/ext/jmol/JalviewJmolBindingTest.java [deleted file]
test/jalview/ext/jmol/JmolCommandsTest.java
test/jalview/ext/pymol/PymolCommandsTest.java
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java
test/jalview/ext/rbvi/chimera/ChimeraXCommandsTest.java
test/jalview/ext/rbvi/chimera/JalviewChimeraBindingTest.java [deleted file]
test/jalview/structures/models/AAStructureBindingModelTest.java

index 6f14e21..bffd4a4 100644 (file)
@@ -31,7 +31,7 @@ import java.util.regex.PatternSyntaxException;
 
 /**
  * Data class holding the selected columns and hidden column ranges for a view.
- * Ranges are base 1.
+ * Column positions are base 0.
  */
 public class ColumnSelection
 {
index 09f3876..d5a0347 100644 (file)
@@ -39,7 +39,6 @@ import org.jmol.api.JmolViewer;
 import org.jmol.c.CBK;
 import org.jmol.viewer.Viewer;
 
-import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
@@ -51,7 +50,6 @@ import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
 import jalview.structure.AtomSpec;
-import jalview.structure.AtomSpecModel;
 import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
 import jalview.structure.StructureSelectionManager;
@@ -1000,75 +998,6 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     showConsole(false);
   }
 
-  @Override
-  public void showStructures(AlignViewportI av, boolean refocus)
-  {
-    String cmd = buildShowStructuresCommand(av, refocus);
-    executeCommand(new StructureCommand(cmd), false);
-  }
-
-  /**
-   * Builds a command to show parts of the structure, depending on whether
-   * <ul>
-   * <li>all structures or regions mapped to alignment only are shown</li>
-   * <li>all chains or only selected chains are shown</li>
-   * </ul>
-   * 
-   * @param av
-   * @param refocus
-   * @return
-   */
-  protected String buildShowStructuresCommand(AlignViewportI av,
-          boolean refocus)
-  {
-    StringBuilder cmd = new StringBuilder(128);
-    if (!isShowAlignmentOnly())
-    {
-      cmd.append("display *");
-    }
-    else
-    {
-      AtomSpecModel model = getShownResidues(av);
-      String atomSpec = getCommandGenerator().getAtomSpec(model, false);
-
-      cmd.append("hide *;display ").append(atomSpec)
-              .append("; select displayed");
-    }
-
-    /*
-     * hide any chains not selected to be shown
-     */
-    if (!chainsToHide.isEmpty())
-    {
-      cmd.append("; hide add ");
-      boolean firstHide = true;
-      for (String pdbChain : chainsToHide)
-      {
-        String[] toks = pdbChain.split(":");
-        String chainId = toks[1];
-        String modelNo = getModelIdForFile(getFileForChain(pdbChain));
-        if ("".equals(modelNo))
-        {
-          continue;
-        }
-        if (!firstHide)
-        {
-          cmd.append(",");
-        }
-        firstHide = false;
-        cmd.append(":").append(chainId).append("/")
-                .append(String.valueOf(modelNo)).append(".1");
-      }
-    }
-
-    cmd.append("; cartoon only");
-    if (refocus)
-    {
-      cmd.append("; zoom 0");
-    }
-    return cmd.toString();
-  }
-
   /**
    * Answers a Jmol syntax style structure model specification. Model number 0, 1,
    * 2... is formatted as "1.1", "2.1", "3.1" etc.
index 35ee8d2..1340822 100644 (file)
@@ -122,31 +122,6 @@ public class JmolCommands extends StructureCommandsBase
     return FOCUS_VIEW;
   }
 
-  @Override
-  public List<StructureCommandI> showChains(List<String> toShow)
-  {
-    StringBuilder atomSpec = new StringBuilder(128);
-    boolean first = true;
-    for (String chain : toShow)
-    {
-      String[] tokens = chain.split(":");
-      if (tokens.length == 2)
-      {
-        if (!first)
-        {
-          atomSpec.append(" or ");
-        }
-        first = false;
-        atomSpec.append(":").append(tokens[1]).append(" /").append(tokens[0]);
-      }
-    }
-
-    String spec = atomSpec.toString();
-    String command = "select *;restrict " + spec + ";cartoon;center "
-            + spec;
-    return Arrays.asList(new StructureCommand(command));
-  }
-
   /**
    * Returns a command to superpose atoms in {@code atomSpec} to those in
    * {@code refAtoms}, restricted to alpha carbons only (Phosphorous for rna).
@@ -483,4 +458,28 @@ public class JmolCommands extends StructureCommandsBase
   {
     return loadFile(filepath);
   }
+
+  @Override
+  public StructureCommandI showStructures(AtomSpecModel restrictTo)
+  {
+    if (restrictTo == null)
+    {
+      return new StructureCommand("display *; cartoon only");
+    }
+    String atomSpec = getAtomSpec(restrictTo, false);
+    String cmd = "display " + atomSpec + "; select displayed; cartoon only";
+    return new StructureCommand(cmd);
+  }
+
+  @Override
+  public StructureCommandI hideChain(String modelId, String chainId)
+  {
+    return new StructureCommand("hide add :" + chainId + "/" + modelId);
+  }
+
+  @Override
+  public StructureCommandI hideAll()
+  {
+    return new StructureCommand("hide *");
+  }
 }
index 3493d03..ff7d0b2 100644 (file)
@@ -22,6 +22,12 @@ import jalview.structure.StructureCommandsBase;
  */
 public class PymolCommands extends StructureCommandsBase
 {
+  private static final StructureCommand SHOW_RIBBON = new StructureCommand("show", "ribbon");
+
+  private static final StructureCommand SHOW_CARTOON = new StructureCommand("show", "cartoon");
+
+  private static final StructureCommand HIDE_EVERYTHING = new StructureCommand("hide", "everything");
+
   private static final StructureCommand COLOUR_BY_CHAIN = new StructureCommand("spectrum", "chain");
 
   private static final List<StructureCommandI> COLOR_BY_CHARGE = new ArrayList<>();
@@ -36,8 +42,8 @@ public class PymolCommands extends StructureCommandsBase
             new StructureCommand("color", "blue", "resn LYS resn ARG"));
     COLOR_BY_CHARGE
             .add(new StructureCommand("color", "yellow", "resn CYS"));
-    SHOW_BACKBONE.add(new StructureCommand("hide", "everything"));
-    SHOW_BACKBONE.add(new StructureCommand("show", "ribbon"));
+    SHOW_BACKBONE.add(HIDE_EVERYTHING);
+    SHOW_BACKBONE.add(SHOW_RIBBON);
   }
 
   @Override
@@ -80,22 +86,6 @@ public class PymolCommands extends StructureCommandsBase
   }
 
   @Override
-  public List<StructureCommandI> showChains(List<String> toShow)
-  {
-    // https://pymolwiki.org/index.php/Show
-    List<StructureCommandI> commands = new ArrayList<>();
-    commands.add(new StructureCommand("hide", "everything"));
-    commands.add(new StructureCommand("show", "lines"));
-    StringBuilder chains = new StringBuilder();
-    for (String chain : toShow)
-    {
-      chains.append(" chain ").append(chain);
-    }
-    commands.add(new StructureCommand("show", "cartoon", chains.toString()));
-    return commands;
-  }
-
-  @Override
   public List<StructureCommandI> superposeStructures(AtomSpecModel refAtoms,
           AtomSpecModel atomSpec)
   {
@@ -317,4 +307,30 @@ public class PymolCommands extends StructureCommandsBase
     return new StructureCommand("load", filepath, "", "0", "pse");
   }
 
+  @Override
+  public StructureCommandI showStructures(AtomSpecModel restrictTo)
+  {
+    if (restrictTo == null)
+    {
+      return SHOW_CARTOON;
+    }
+    else
+    {
+      return new StructureCommand("show", "cartoon",
+              getAtomSpec(restrictTo, false));
+    }
+  }
+
+  @Override
+  public StructureCommandI hideChain(String modelId, String chainId)
+  {
+    return new StructureCommand("hide", modelId + "//" + chainId + "//");
+  }
+
+  @Override
+  public StructureCommandI hideAll()
+  {
+    return HIDE_EVERYTHING;
+  }
+
 }
index 642035f..8c6bafb 100644 (file)
@@ -36,6 +36,7 @@ import jalview.util.ColorUtils;
  * Routines for generating Chimera commands for Jalview/Chimera binding
  * 
  * @author JimP
+ * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/framecommand.html
  * 
  */
 public class ChimeraCommands extends StructureCommandsBase
@@ -205,41 +206,6 @@ public class ChimeraCommands extends StructureCommandsBase
   }
 
   @Override
-  public List<StructureCommandI> showChains(List<String> toShow)
-  {
-    /*
-     * Construct a chimera command like
-     * 
-     * ~display #*;~ribbon #*;ribbon :.A,:.B
-     */
-    StringBuilder cmd = new StringBuilder(64);
-    boolean first = true;
-    for (String chain : toShow)
-    {
-      String[] tokens = chain.split(":");
-      if (tokens.length == 2)
-      {
-        String showChainCmd = tokens[0] + ":." + tokens[1];
-        if (!first)
-        {
-          cmd.append(",");
-        }
-        cmd.append(showChainCmd);
-        first = false;
-      }
-    }
-
-    /*
-     * could append ";focus" to this command to resize the display to fill the
-     * window, but it looks more helpful not to (easier to relate chains to the
-     * whole)
-     */
-    final String command = "~display #*; ~ribbon #*; ribbon :"
-            + cmd.toString();
-    return Arrays.asList(new StructureCommand(command));
-  }
-
-  @Override
   public List<StructureCommandI> superposeStructures(AtomSpecModel ref,
           AtomSpecModel spec)
   {
@@ -407,4 +373,30 @@ public class ChimeraCommands extends StructureCommandsBase
     return new StructureCommand("open chimera:" + filepath);
   }
 
+  @Override
+  public StructureCommandI showStructures(AtomSpecModel restrictTo)
+  {
+    if (restrictTo == null)
+    {
+      return new StructureCommand("ribbon");
+    }
+
+    String atomSpec = getAtomSpec(restrictTo, false);
+    String cmd = "ribbon " + atomSpec;
+    return new StructureCommand(cmd);
+  }
+
+  @Override
+  public StructureCommandI hideChain(String modelId, String chainId)
+  {
+    String cmd = "~ribbon #" + modelId + ":." + chainId;
+    return new StructureCommand(cmd);
+  }
+
+  @Override
+  public StructureCommandI hideAll()
+  {
+    return new StructureCommand("~display; ~ribbon");
+  }
+
 }
index 8a4a299..dee6e49 100644 (file)
@@ -27,7 +27,6 @@ import java.util.List;
 import jalview.structure.AtomSpecModel;
 import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
-import jalview.util.ColorUtils;
 
 /**
  * Routines for generating ChimeraX commands for Jalview/ChimeraX binding
@@ -212,4 +211,11 @@ public class ChimeraXCommands extends ChimeraCommands
     // this version of the command has no dependency on file extension
     return new StructureCommand("open " + filepath + " format session");
   }
+
+  @Override
+  public StructureCommandI hideChain(String modelId, String chainId)
+  {
+    String cmd = "~ribbon #" + modelId + "/" + chainId;
+    return new StructureCommand(cmd);
+  }
 }
index 0907695..718db79 100644 (file)
@@ -36,7 +36,6 @@ import ext.edu.ucsf.rbvi.strucviz2.ChimeraManager;
 import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel;
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
-import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
@@ -832,68 +831,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   }
 
   @Override
-  public void showStructures(AlignViewportI av, boolean refocus)
-  {
-    StructureCommandI cmd = buildShowStructuresCommand(av, refocus);
-    executeCommand(cmd, false);
-  }
-
-  /**
-   * Builds a command to show parts of the structure, depending on whether
-   * <ul>
-   * <li>all structures or regions mapped to alignment only are shown</li>
-   * <li>all chains or only selected chains are shown</li>
-   * </ul>
-   * 
-   * @param av
-   * @param refocus
-   * @return
-   */
-  protected StructureCommandI buildShowStructuresCommand(
-          AlignViewportI av,
-          boolean refocus)
-  {
-    // TODO refactor using command generator
-    // pull up this method and Jmol variant to base class
-    StringBuilder cmd = new StringBuilder(128);
-    cmd.append("~display");
-
-    if (isShowAlignmentOnly())
-    {
-      AtomSpecModel model = getShownResidues(av);
-      String atomSpec = getCommandGenerator().getAtomSpec(model, false);
-      if (!atomSpec.isEmpty())
-      {
-        cmd.append("; ~ribbon; ribbon ").append(atomSpec);
-      }
-    }
-    else
-    {
-      cmd.append("; ribbon");
-    }
-
-    /*
-     * hide any chains selected not to be shown (whether mapped to
-     * sequence in the alignment or not)
-     */
-    for (String pdbChain : chainsToHide)
-    {
-      String chainId = pdbChain.split(":")[1];
-      String modelNo = getModelIdForFile(getFileForChain(pdbChain));
-      if (!"".equals(modelNo))
-      {
-        cmd.append("; ~ribbon #").append(modelNo).append(":.")
-                .append(chainId);
-      }
-    }
-    if (refocus)
-    {
-      cmd.append("; focus");
-    }
-    return new StructureCommand(cmd.toString());
-  }
-
-  @Override
   public int getModelForPdbFile(String fileName, int fileIndex)
   {
     if (chimeraMaps.containsKey(fileName))
index c5a4c9a..8a4ce08 100644 (file)
@@ -250,7 +250,7 @@ public class PymolViewer extends StructureViewerBase
           } catch (Exception ex)
           {
             Cache.log.error(
-                    "Couldn't open " + pe.getFile() + " in Chimera viewer!",
+                    "Couldn't open " + pe.getFile() + " in Pymol viewer!",
                     ex);
           } finally
           {
index 900252a..77ba38d 100644 (file)
@@ -513,6 +513,10 @@ public abstract class StructureViewerBase extends GStructureViewer
     return true;
   }
 
+  /**
+   * Add menu items to the 'Show Chains' sub-menu, given a list of values formatted as "pdbId:chainCode"
+   * @param chainNames
+   */
   void setChainMenuItems(List<String> chainNames)
   {
     chainMenu.removeAll();
@@ -549,10 +553,15 @@ public abstract class StructureViewerBase extends GStructureViewer
      * add a menu item for each structure and chain
      */
     Collections.sort(chainNames);
+    boolean hideUnmapped = getBinding().isShowAlignmentOnly();
     for (String chain : chainNames)
     {
+      String[] tokens = chain.split(":");
+      String pdbId = tokens[0];
+      String chainCode = tokens[1];
       String seqName = getSequenceNameForChain(chain);
-      if (seqName == null)
+      boolean isUnmapped = seqName == null;
+      if (isUnmapped)
       {
         seqName = UNMAPPED;
       }
@@ -564,6 +573,15 @@ public abstract class StructureViewerBase extends GStructureViewer
       }
       String text = chain + " " + seqName;
       menuItem = new JCheckBoxMenuItem(text, true);
+      
+      /*
+       * set the item's state _before_ adding its listener!
+       */
+      menuItem.setSelected(getBinding().isShowChain(pdbId,  chainCode));
+      if (hideUnmapped && isUnmapped)
+      {
+        menuItem.setEnabled(false);
+      }
       menuItem.addItemListener(new ItemListener()
       {
         @Override
@@ -575,7 +593,6 @@ public abstract class StructureViewerBase extends GStructureViewer
           }
         }
       });
-
       chainMenu.add(menuItem);
     }
   }
@@ -767,6 +784,7 @@ public abstract class StructureViewerBase extends GStructureViewer
         getBinding().setShowAlignmentOnly(showAlignmentOnly.isSelected());
         getBinding().showStructures(getAlignmentPanel().getAlignViewport(),
                 true);
+        updateTitleAndMenus(); // to add or remove unmapped chain menu items
       }
     });
     viewMenu.add(showAlignmentOnly);
index 2169184..2c3d061 100644 (file)
@@ -70,15 +70,6 @@ public interface StructureCommandsI
   StructureCommandI focusView();
 
   /**
-   * Returns a command to show only the selected chains. The items in the input
-   * list should be formatted as "modelid:chainid".
-   * 
-   * @param toShow
-   * @return
-   */
-  List<StructureCommandI> showChains(List<String> toShow);
-
-  /**
    * Returns a command to superpose structures by closest positioning of
    * residues in {@code atomSpec} to the corresponding residues in
    * {@code refAtoms}. If wanted, this may include commands to visually
@@ -156,4 +147,30 @@ public interface StructureCommandsI
    * @return
    */
   StructureCommandI openSession(String filepath);
+
+  /**
+   * Returns a command to show structures in the viewer. If {@code restrictTo}
+   * is null, all structures are included, otherwise the display is restricted
+   * to positions represented in the model
+   * 
+   * @param restrictTo
+   * @return
+   */
+  StructureCommandI showStructures(AtomSpecModel restrictTo);
+
+  /**
+   * Returns a command to hide the specified model chain in the structure viewer
+   * 
+   * @param modelId
+   * @param chainId
+   * @return
+   */
+  StructureCommandI hideChain(String modelId, String chainId);
+
+  /**
+   * Returns a command to hide everything in the structure viewer
+   * 
+   * @return
+   */
+  StructureCommandI hideAll();
 }
index 7b1485c..b23424c 100644 (file)
@@ -420,6 +420,7 @@ public abstract class AAStructureBindingModel
    */
   public void refreshGUI()
   {
+    getViewer().updateTitleAndMenus();
   }
 
   /**
@@ -1089,6 +1090,10 @@ public abstract class AAStructureBindingModel
   private List<String> executeCommand(StructureCommandI cmd,
           boolean getReply, String msg)
   {
+    if (cmd == null)
+    {
+      return null; // catch unimplemented commands
+    }
     if (getReply)
     {
       /*
@@ -1244,36 +1249,6 @@ public abstract class AAStructureBindingModel
   }
 
   /**
-   * Generates and executes a command to show only specified chains in the
-   * structure viewer. The list of chains to show should contain entries
-   * formatted as "pdbid:chaincode".
-   * 
-   * @param toShow
-   */
-  public void showChains(List<String> toShow)
-  {
-    // todo or reformat toShow list entries as modelNo:pdbId:chainCode ?
-
-    /*
-     * Reformat the pdbid:chainCode values as modelNo:chainCode
-     * since this is what is needed to construct the viewer command
-     * todo: find a less messy way to do this
-     */
-    List<String> showThese = new ArrayList<>();
-    for (String chainId : toShow)
-    {
-      String[] tokens = chainId.split("\\:");
-      if (tokens.length == 2)
-      {
-        String pdbFile = getFileForChain(chainId);
-        String model = getModelIdForFile(pdbFile);
-        showThese.add(model + ":" + tokens[1]);
-      }
-    }
-    executeCommands(commandGenerator.showChains(showThese), false, null);
-  }
-
-  /**
    * Answers the structure viewer's model id given a PDB file name. Returns an
    * empty string if model id is not found.
    * 
@@ -1334,17 +1309,47 @@ public abstract class AAStructureBindingModel
   }
 
   /**
-   * Shows the structures in the viewer, without changing their colouring. This is
-   * to support toggling of whether the whole structure is shown, or only residues
-   * mapped to visible regions of the alignment.
+   * Shows the structures in the viewer, without changing their colouring. This
+   * is to support toggling of whether the whole structure is shown, or only
+   * residues mapped to visible regions of the alignment, and/or only selected
+   * chains.
    * 
-   * @param alignViewportI
+   * @param av
    * @param refocus
-   *                         if true, refit the display to the viewer
+   *          if true, rescale the display to the viewer
    */
-  public void showStructures(AlignViewportI alignViewportI, boolean refocus)
+  public void showStructures(AlignViewportI av, boolean refocus)
   {
-    // override with implementation
+    if (isShowAlignmentOnly())
+    {
+      StructureCommandI cmd = getCommandGenerator().hideAll();
+      executeCommand(cmd, false);
+    }
+
+    AtomSpecModel model = isShowAlignmentOnly() ? getShownResidues(av)
+            : null;
+    StructureCommandI cmd = getCommandGenerator().showStructures(model);
+    executeCommand(cmd, false);
+
+    /*
+     * and hide any chains selected _not_ to be shown 
+     * (whether mapped to sequence in the alignment or not)
+     */
+    for (String pdbChain : chainsToHide)
+    {
+      String modelNo = getModelIdForFile(getFileForChain(pdbChain));
+      if (!"".equals(modelNo))
+      {
+        String chainId = pdbChain.split(":")[1];
+        cmd = getCommandGenerator().hideChain(modelNo, chainId);
+        executeCommand(cmd, false);
+      }
+    }
+
+    if (refocus)
+    {
+      focusView();
+    }
   }
 
   /**
@@ -1365,7 +1370,7 @@ public abstract class AAStructureBindingModel
    * @param chainId
    * @return
    */
-  protected boolean isShowChain(String pdbId, String chainId)
+  public boolean isShowChain(String pdbId, String chainId)
   {
     if (chainsToHide.isEmpty())
     {
@@ -1378,13 +1383,12 @@ public abstract class AAStructureBindingModel
   public abstract String[] getStructureFiles();
 
   /**
-   * Builds a model of residues mapped from sequences to show on structure, taking
-   * into account user choices of
+   * Builds a model of residues mapped from sequences to show on structure,
+   * taking into account user choices of
    * <ul>
+   * <li>whether hidden regions of the alignment are excluded (hidden) or
+   * included (greyed out)</li>
    * <li>which chains are shown</li>
-   * <li>whether all structure is shown, or only that mapped to the alignment</li>
-   * <li>whether hidden regions of the alignment are hidden (excluded) or grayed
-   * out (included)</li>
    * </ul>
    * 
    * @param av
@@ -1392,6 +1396,13 @@ public abstract class AAStructureBindingModel
    */
   protected AtomSpecModel getShownResidues(AlignViewportI av)
   {
+    if (!isShowAlignmentOnly())
+    {
+      Cache.log.error(
+              "getShownResidues only valid for 'show alignment only')");
+      return null;
+    }
+
     AlignmentI alignment = av.getAlignment();
     final int width = alignment.getWidth();
   
@@ -1421,16 +1432,26 @@ public abstract class AAStructureBindingModel
             String chainCd = mapping.getChain();
             if (!isShowChain(mapping.getPdbId(), chainCd))
             {
-              // continue;
+              /*
+               * chain is not selected to be displayed, so don't
+               * waste effort computing its structure positions
+               */
+              continue;
             }
             Iterator<int[]> visible;
-            if (isShowAlignmentOnly() && isHideHiddenRegions())
+            if (isHideHiddenRegions())
             {
+              /*
+               * traverse visible columns of the alignment only
+               */
               visible = alignment.getHiddenColumns()
                     .getVisContigsIterator(0, width, true);
             }
             else
             {
+              /*
+               * traverse all columns (including hidden if any)
+               */
               visible = Collections.singletonList(new int[] { 0, width })
                       .iterator();
             }
diff --git a/test/jalview/ext/jmol/JalviewJmolBindingTest.java b/test/jalview/ext/jmol/JalviewJmolBindingTest.java
deleted file mode 100644 (file)
index 0a0b634..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-package jalview.ext.jmol;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.gui.AlignViewport;
-import jalview.gui.AppJmolBinding;
-import jalview.io.DataSourceType;
-import jalview.io.FileLoader;
-import jalview.structure.StructureMapping;
-import jalview.structure.StructureSelectionManager;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.testng.annotations.BeforeTest;
-import org.testng.annotations.Test;
-
-import junit.extensions.PA;
-
-public class JalviewJmolBindingTest
-{
-  private AlignFrame af;
-
-  @BeforeTest(alwaysRun = true)
-  public void setup()
-  {
-    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
-            DataSourceType.FILE);
-  }
-
-  @Test(groups = "Functional")
-  public void testBuildShowStructuresCommand()
-  {
-    AlignViewport av = af.getViewport();
-    PDBEntry[] pdbs = new PDBEntry[] {};
-    SequenceI seq1 = av.getAlignment().findSequenceMatch("FER1_SPIOL")[0];
-    assertNotNull(seq1);
-    SequenceI seq2 = av.getAlignment().findSequenceMatch("FER2_ARATH")[0];
-    assertNotNull(seq2);
-    StructureSelectionManager ssm = new StructureSelectionManager();
-    SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
-    AppJmolBinding testee = new AppJmolBinding(null, ssm, pdbs, seqs,
-            null);
-
-    /*
-     * map FER1_SPIOL residues 51-100 to residues 1-50 (atoms 1-250) in 1A70
-     * and residues 110-147 to structure residues 60-97
-     * (in fact there is no gap, added here for test purposes)
-     */
-    HashMap<Integer, int[]> map = new HashMap<>();
-    for (int pos = 51; pos <= 100; pos++)
-    {
-      map.put(pos, new int[] { pos - 50, 5 * (pos - 50) });
-    }
-    for (int pos = 110; pos <= 147; pos++)
-    {
-      map.put(pos, new int[] { pos - 50, 5 * (pos - 50) });
-    }
-    StructureMapping sm1 = new StructureMapping(seq1, "1a70.pdb", "1A70",
-            "A", map, null);
-    ssm.addStructureMapping(sm1);
-
-    /*
-     * map FER2_ARATH residues 53-148 to residues 2-97 in 4ZHO
-     */
-    map = new HashMap<>();
-    for (int pos = 53; pos <= 148; pos++)
-    {
-      map.put(pos, new int[] { pos - 51, 5 * (pos - 51) });
-    }
-    StructureMapping sm2 = new StructureMapping(seq2, "4zho.pdb", "4ZHO",
-            "B", map, null);
-    ssm.addStructureMapping(sm2);
-
-    /*
-     * show everything
-     */
-    String cmd = testee.buildShowStructuresCommand(av, true);
-    assertEquals(cmd, "display *; cartoon only; zoom 0");
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd, "display *; cartoon only");
-
-    /*
-     * stub out modelFileNames - array index is Jmol
-     * model number - 1
-     */
-    PA.setValue(testee, "modelFileNames",
-            new String[]
-            { "1a70.pdb", "4zho.pdb" });
-
-    /*
-     * stub out lookup map from pdb:chain to filename
-     */
-    Map<String, String> chainFiles = new HashMap<>();
-    PA.setValue(testee, "chainFile", chainFiles);
-    chainFiles.put("1A70:A", "1a70.pdb");
-    chainFiles.put("1A70:B", "1a70.pdb");
-    chainFiles.put("4ZHO:B", "4zho.pdb");
-    chainFiles.put("4ZHO:C", "4zho.pdb");
-
-    /*
-     * show all except for selected chains to hide
-     */
-    List<String> chainsToHide = (List<String>) PA.getValue(testee,
-            "chainsToHide");
-    chainsToHide.add("1A70:B");
-    chainsToHide.add("4ZHO:C");
-    cmd = testee.buildShowStructuresCommand(av, true);
-    assertEquals(cmd,
-            "display *; hide add :B/1.1,:C/2.1; cartoon only; zoom 0");
-
-    /*
-     * show alignment only, no chains hidden
-     */
-    chainsToHide.clear();
-    testee.setShowAlignmentOnly(true);
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd,
-            "hide *;display (1-50,60-97)&:A/1.1,2-97:B/2.1; select displayed; cartoon only");
-
-    /*
-     * now with a chain hidden
-     */
-    chainsToHide.add("4ZHO:C");
-    cmd = testee.buildShowStructuresCommand(av, false);
-    String expected = "hide *;display (1-50,60-97)&:A/1.1,2-97:B/2.1; select displayed; hide add :C/2.1; cartoon only";
-    assertEquals(cmd, expected);
-
-    /*
-     * hide columns in the mapped region - should not change the command (yet)
-     */
-    int fromCol = seq1.findIndex(60); // structure residue 10
-    int toCol = seq1.findIndex(70); // structure residue 20
-    av.hideColumns(fromCol - 1, toCol - 1);
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd, expected);
-
-    /*
-     * select 'hide hidden columns'
-     * command should now exclude these in both mapped sequences
-     */
-    testee.setHideHiddenRegions(true);
-    cmd = testee.buildShowStructuresCommand(av, false);
-    expected = "hide *;display (1-9,21-50,60-97)&:A/1.1,(2-10,22-97)&:B/2.1; select displayed; hide add :C/2.1; cartoon only";
-    assertEquals(cmd, expected);
-
-    /*
-     * deselect 'show alignment only'
-     * hide hidden columns is now ignored
-     */
-    testee.setShowAlignmentOnly(false);
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd, "display *; hide add :C/2.1; cartoon only");
-  }
-}
index cbb9e10..95b7e51 100644 (file)
@@ -304,4 +304,39 @@ public class JmolCommandsTest
     cmd = testee.openSession("\\some\\filepath");
     assertEquals(cmd.getCommand(), "load FILES \"\\\\some\\\\filepath\"");
   }
+
+  @Test(groups = "Functional")
+  public void testHideAll()
+  {
+    StructureCommandI cmd = testee.hideAll();
+    assertEquals(cmd.getCommand(), "hide *");
+  }
+
+  @Test(groups = "Functional")
+  public void testHideChain()
+  {
+    StructureCommandI cmd = testee.hideChain("1.1", "B");
+    assertEquals(cmd.getCommand(), "hide add :B/1.1");
+  }
+
+  @Test(groups = "Functional")
+  public void testShowStructures()
+  {
+    /*
+     * with nothing excluded
+     */
+    StructureCommandI cmd = testee.showStructures(null);
+    assertEquals(cmd.getCommand(), "display *; cartoon only");
+
+    /*
+     * restricted to specified positions
+     */
+    AtomSpecModel restrictTo = new AtomSpecModel();
+    restrictTo.addRange("1.1", 12, 20, "A");
+    restrictTo.addRange("1.1", 30, 35, "A");
+    restrictTo.addRange("2.1", 11, 30, "B");
+    cmd = testee.showStructures(restrictTo);
+    assertEquals(cmd.getCommand(),
+            "display (12-20,30-35)&:A/1.1.1,11-30:B/2.1.1; select displayed; cartoon only");
+  }
 }
index 90d0cc7..8beee96 100644 (file)
@@ -335,4 +335,39 @@ public class PymolCommandsTest
             "p.jv_side_chain_binding_='<html>metal <a href=\"http:a.b.c/x\"> &#39;ion!'");
     assertEquals(commands.get(0), expected3);
   }
+
+  @Test(groups = "Functional")
+  public void testHideAll()
+  {
+    StructureCommandI cmd = testee.hideAll();
+    assertEquals(cmd, new StructureCommand("hide", "everything"));
+  }
+
+  @Test(groups = "Functional")
+  public void testHideChain()
+  {
+    StructureCommandI cmd = testee.hideChain("1.1", "B");
+    assertEquals(cmd, new StructureCommand("hide", "1.1//B//"));
+  }
+
+  @Test(groups = "Functional")
+  public void testShowStructures()
+  {
+    /*
+     * with nothing excluded
+     */
+    StructureCommandI cmd = testee.showStructures(null);
+    assertEquals(cmd, new StructureCommand("show", "cartoon"));
+
+    /*
+     * restricted to specified positions
+     */
+    AtomSpecModel restrictTo = new AtomSpecModel();
+    restrictTo.addRange("1.1", 12, 20, "A");
+    restrictTo.addRange("1.1", 30, 35, "A");
+    restrictTo.addRange("2.1", 11, 30, "B");
+    cmd = testee.showStructures(restrictTo);
+    assertEquals(cmd, new StructureCommand("show", "cartoon",
+            "1.1//A/12-20+30-35/ 2.1//B/11-30/"));
+  }
 }
index b8ca2dd..fd4ec69 100644 (file)
@@ -61,11 +61,10 @@ public class ChimeraCommandsTest
 
     // Colours should appear in the Chimera command in the order in which
     // they were added; within colour, by model, by chain, ranges in start order
-    // all prefixed with #808080 to colour hidden regions (if shown) gray
     List<StructureCommandI> commands = testee.colourBySequence(map);
     assertEquals(commands.size(), 1);
     assertEquals(commands.get(0).getCommand(),
-            "color #808080; color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B;color #ffff00 #1:3-5.A,8.A;color #ff0000 #0:3-9.A");
+            "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B;color #ffff00 #1:3-5.A,8.A;color #ff0000 #0:3-9.A");
   }
 
   @Test(groups = { "Functional" })
@@ -340,4 +339,39 @@ public class ChimeraCommandsTest
     assertEquals(testee.setAttribute("jv_kd", "27.3", model).getCommand(),
             "setattr res jv_kd '27.3' #1:89-92.A|#2:8-9.B,12-20.B");
   }
+
+  @Test(groups = "Functional")
+  public void testHideAll()
+  {
+    StructureCommandI cmd = testee.hideAll();
+    assertEquals(cmd.getCommand(), "~display all; ~ribbon");
+  }
+
+  @Test(groups = "Functional")
+  public void testHideChain()
+  {
+    StructureCommandI cmd = testee.hideChain("1.1", "B");
+    assertEquals(cmd.getCommand(), "~ribbon #1.1:.B");
+  }
+
+  @Test(groups = "Functional")
+  public void testShowStructures()
+  {
+    /*
+     * with nothing excluded
+     */
+    StructureCommandI cmd = testee.showStructures(null);
+    assertEquals(cmd.getCommand(), "ribbon");
+
+    /*
+     * restricted to specified positions
+     */
+    AtomSpecModel restrictTo = new AtomSpecModel();
+    restrictTo.addRange("1.1", 12, 20, "A");
+    restrictTo.addRange("1.1", 30, 35, "A");
+    restrictTo.addRange("2.1", 11, 30, "B");
+    cmd = testee.showStructures(restrictTo);
+    assertEquals(cmd.getCommand(),
+            "ribbon #1.1:12-20.A,30-35.A|#2.1:11-30.B");
+  }
 }
index 02a9dd5..16ee0b1 100644 (file)
@@ -316,4 +316,39 @@ public class ChimeraXCommandsTest
             .getCommand(),
             "setattr #1/A:89-92|#2/B:8-9,12-20 res jv_kd '27.3' create true");
   }
+
+  @Test(groups = "Functional")
+  public void testHideAll()
+  {
+    StructureCommandI cmd = testee.hideAll();
+    assertEquals(cmd.getCommand(), "~display all; ~ribbon");
+  }
+
+  @Test(groups = "Functional")
+  public void testHideChain()
+  {
+    StructureCommandI cmd = testee.hideChain("1.1", "B");
+    assertEquals(cmd.getCommand(), "~ribbon #1.1/B");
+  }
+
+  @Test(groups = "Functional")
+  public void testShowStructures()
+  {
+    /*
+     * with nothing excluded
+     */
+    StructureCommandI cmd = testee.showStructures(null);
+    assertEquals(cmd.getCommand(), "ribbon");
+
+    /*
+     * restricted to specified positions
+     */
+    AtomSpecModel restrictTo = new AtomSpecModel();
+    restrictTo.addRange("1.1", 12, 20, "A");
+    restrictTo.addRange("1.1", 30, 35, "A");
+    restrictTo.addRange("2.1", 11, 30, "B");
+    cmd = testee.showStructures(restrictTo);
+    assertEquals(cmd.getCommand(),
+            "ribbon #1.1/A:12-20,30-35|#2.1/B:11-30");
+  }
 }
diff --git a/test/jalview/ext/rbvi/chimera/JalviewChimeraBindingTest.java b/test/jalview/ext/rbvi/chimera/JalviewChimeraBindingTest.java
deleted file mode 100644 (file)
index fde4bb5..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-package jalview.ext.rbvi.chimera;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.testng.annotations.BeforeTest;
-import org.testng.annotations.Test;
-
-import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel;
-import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.gui.AlignViewport;
-import jalview.gui.ChimeraViewFrame;
-import jalview.gui.JalviewChimeraBindingModel;
-import jalview.io.DataSourceType;
-import jalview.io.FileLoader;
-import jalview.structure.StructureCommandI;
-import jalview.structure.StructureMapping;
-import jalview.structure.StructureSelectionManager;
-import junit.extensions.PA;
-
-public class JalviewChimeraBindingTest
-{
-  private AlignFrame af;
-
-  @BeforeTest(alwaysRun = true)
-  public void setup()
-  {
-    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
-            DataSourceType.FILE);
-  }
-
-  @Test(groups = "Functional")
-  public void testBuildShowStructuresCommand()
-  {
-    AlignViewport av = af.getViewport();
-    PDBEntry[] pdbs = new PDBEntry[] {};
-    StructureSelectionManager ssm = new StructureSelectionManager();
-    SequenceI seq1 = av.getAlignment().findSequenceMatch("FER1_SPIOL")[0];
-    assertNotNull(seq1);
-    SequenceI seq2 = av.getAlignment().findSequenceMatch("FER2_ARATH")[0];
-    assertNotNull(seq2);
-    SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
-    JalviewChimeraBindingModel testee = new JalviewChimeraBindingModel(
-            new ChimeraViewFrame(),
-            ssm, pdbs, seqs, null);
-
-    /*
-     * with no structures mapped
-     */
-    StructureCommandI cmd = testee.buildShowStructuresCommand(av, true);
-    assertEquals(cmd.getCommand(), "~display; ribbon; focus");
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd.getCommand(), "~display; ribbon");
-
-    /*
-     * stub out a structure with chains A and B
-     */
-    Map<String, List<ChimeraModel>> chimeraMaps = (Map<String, List<ChimeraModel>>) PA
-            .getValue(testee, "chimeraMaps");
-    ChimeraModel model0 = new ChimeraModel("1A70", ModelType.PDB_MODEL, 0,
-            0);
-    chimeraMaps.put("1a70.pdb", Arrays.asList(model0));
-    ChimeraModel model1 = new ChimeraModel("4ZHO", ModelType.PDB_MODEL, 1,
-            0);
-    chimeraMaps.put("4zho.pdb", Arrays.asList(model1));
-
-    Map<String, String> chainFiles = (Map<String, String>) PA
-            .getValue(testee, "chainFile");
-    chainFiles.put("1A70:A", "1a70.pdb");
-    chainFiles.put("1A70:B", "1a70.pdb");
-    chainFiles.put("4ZHO:B", "4zho.pdb");
-    chainFiles.put("4ZHO:C", "4zho.pdb");
-
-    /*
-     * map FER1_SPIOL residues 51-100 to residues 1-50 (atoms 1-250) in 1A70
-     * and residues 110-147 to structure residues 60-97
-     * (in fact there is no gap, added here for test purposes)
-     */
-    HashMap<Integer, int[]> map = new HashMap<>();
-    for (int pos = 51; pos <= 100; pos++)
-    {
-      map.put(pos, new int[] { pos - 50, 5 * (pos - 50) });
-    }
-    for (int pos = 110; pos <= 147; pos++)
-    {
-      map.put(pos, new int[] { pos - 50, 5 * (pos - 50) });
-    }
-    StructureMapping sm1 = new StructureMapping(seq1, "1a70.pdb", "1A70",
-            "A", map, null);
-    ssm.addStructureMapping(sm1);
-
-    /*
-     * map FER2_ARATH residues 53-148 to residues 2-97 in 4ZHO
-     */
-    map = new HashMap<>();
-    for (int pos = 53; pos <= 148; pos++)
-    {
-      map.put(pos, new int[] { pos - 51, 5 * (pos - 51) });
-    }
-    StructureMapping sm2 = new StructureMapping(seq2, "4zho.pdb", "4ZHO",
-            "B", map, null);
-    ssm.addStructureMapping(sm2);
-
-    /*
-     * select chain A only (hide chain B)
-     */
-    List<String> chainsToHide = (List<String>) PA.getValue(testee, "chainsToHide");
-    chainsToHide.add("1A70:B");
-    chainsToHide.add("4ZHO:C");
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd.getCommand(),
-            "~display; ribbon; ~ribbon #0:.B; ~ribbon #1:.C");
-
-    /*
-     * show alignment only, no chains hidden
-     */
-    chainsToHide.clear();
-    testee.setShowAlignmentOnly(true);
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd
-            .getCommand(),
-            "~display; ~ribbon; ribbon #0:1-50.A,60-97.A|#1:2-97.B");
-
-    /*
-     * now with a chain hidden
-     */
-    chainsToHide.add("4ZHO:C");
-    cmd = testee.buildShowStructuresCommand(av, false);
-    String expected = "~display; ~ribbon; ribbon #0:1-50.A,60-97.A|#1:2-97.B; ~ribbon #1:.C";
-    assertEquals(cmd.getCommand(), expected);
-
-    /*
-     * hide columns in the mapped region - should not change the command (yet)
-     */
-    int fromCol = seq1.findIndex(60); // structure residue 10
-    int toCol = seq1.findIndex(70); // structure residue 20
-    av.hideColumns(fromCol - 1, toCol - 1);
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd.getCommand(), expected);
-
-    /*
-     * select 'hide hidden columns'
-     * command should now exclude these in both mapped sequences
-     */
-    testee.setHideHiddenRegions(true);
-    cmd = testee.buildShowStructuresCommand(av, false);
-    expected = "~display; ~ribbon; ribbon #0:1-9.A,21-50.A,60-97.A|#1:2-10.B,22-97.B; ~ribbon #1:.C";
-    assertEquals(cmd.getCommand(), expected);
-
-    /*
-     * deselect 'show alignment only'
-     * hide hidden columns is now ignored
-     */
-    testee.setShowAlignmentOnly(false);
-    cmd = testee.buildShowStructuresCommand(av, false);
-    assertEquals(cmd.getCommand(), "~display; ribbon; ~ribbon #1:.C");
-  }
-}
index aea12b4..66d133c 100644 (file)
@@ -126,7 +126,7 @@ public class AAStructureBindingModelTest
 
   // TODO: JAL-2227 - import mmCIF PISA assembly & identify master/copy chains
 
-  @Test(groups= {"Functional"})
+  @Test(groups = { "Functional" })
   public void testImportPDBPreservesChainMappings() throws IOException
   {
     AlignmentI importedAl = new jalview.io.FormatAdapter().readFile(
@@ -136,7 +136,8 @@ public class AAStructureBindingModelTest
     // pasted files,
     // see JAL-623 - pasting is still not correctly handled...
     PDBEntry importedPDB = new PDBEntry("3A6S", "", Type.PDB, "Paste");
-    AAStructureBindingModel binder = newBindingModel(new PDBEntry[]
+    AAStructureBindingModel binder = newBindingModel(
+            new PDBEntry[]
             { importedPDB },
             new SequenceI[][]
             { importedAl.getSequencesArray() },
@@ -149,6 +150,7 @@ public class AAStructureBindingModelTest
     assertEquals(chains[0][0], "A");
     assertEquals(chains[0][1], "B");
   }
+
   AAStructureBindingModel testee;
 
   AlignmentI al = null;
@@ -195,11 +197,11 @@ public class AAStructureBindingModelTest
    * @param pdbFiles
    * @param seqs
    * @param ssm
-   * @param alignPanel 
+   * @param alignPanel
    */
   protected AAStructureBindingModel newBindingModel(PDBEntry[] pdbFiles,
-          SequenceI[][] seqs,
-          StructureSelectionManager ssm, AlignmentViewPanel avp)
+          SequenceI[][] seqs, StructureSelectionManager ssm,
+          AlignmentViewPanel avp)
   {
     AAStructureBindingModel model = new AAStructureBindingModel(ssm,
             pdbFiles, seqs, null)
@@ -236,12 +238,10 @@ public class AAStructureBindingModelTest
       }
 
       @Override
-      public SequenceRenderer getSequenceRenderer(
-              AlignmentViewPanel avp)
+      public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
       {
         return avp == null ? null
-                : new jalview.gui.SequenceRenderer(
-                        avp.getAlignViewport());
+                : new jalview.gui.SequenceRenderer(avp.getAlignViewport());
       }
 
       @Override
@@ -288,10 +288,12 @@ public class AAStructureBindingModelTest
     /*
      * create a data bean to hold data per structure file
      */
-    AAStructureBindingModel.SuperposeData[] structs = new AAStructureBindingModel.SuperposeData[testee.getStructureFiles().length];
+    AAStructureBindingModel.SuperposeData[] structs = new AAStructureBindingModel.SuperposeData[testee
+            .getStructureFiles().length];
     for (int i = 0; i < structs.length; i++)
     {
-      structs[i] = new AAStructureBindingModel.SuperposeData(al.getWidth(), "0");
+      structs[i] = new AAStructureBindingModel.SuperposeData(al.getWidth(),
+              "0");
     }
     /*
      * initialise BitSet of 'superposable columns' to true (would be false for
@@ -303,8 +305,8 @@ public class AAStructureBindingModelTest
       matched.set(i);
     }
 
-    int refStructure = testee
-            .findSuperposableResidues(al, matched, structs);
+    int refStructure = testee.findSuperposableResidues(al, matched,
+            structs);
 
     assertEquals(refStructure, 0);
 
@@ -336,10 +338,12 @@ public class AAStructureBindingModelTest
   @Test(groups = { "Functional" })
   public void testFindSuperposableResidues_hiddenColumn()
   {
-    AAStructureBindingModel.SuperposeData[] structs = new AAStructureBindingModel.SuperposeData[al.getHeight()];
+    AAStructureBindingModel.SuperposeData[] structs = new AAStructureBindingModel.SuperposeData[al
+            .getHeight()];
     for (int i = 0; i < structs.length; i++)
     {
-      structs[i] = new AAStructureBindingModel.SuperposeData(al.getWidth(), "0");
+      structs[i] = new AAStructureBindingModel.SuperposeData(al.getWidth(),
+              "0");
     }
     /*
      * initialise BitSet of 'superposable columns' to true (would be false for
@@ -354,8 +358,8 @@ public class AAStructureBindingModelTest
     // treat column 5 of the alignment as hidden
     matched.clear(4);
 
-    int refStructure = testee
-            .findSuperposableResidues(al, matched, structs);
+    int refStructure = testee.findSuperposableResidues(al, matched,
+            structs);
 
     assertEquals(refStructure, 0);
 
@@ -393,7 +397,7 @@ public class AAStructureBindingModelTest
     pdbFiles[0] = new PDBEntry("PDB1", "A", Type.PDB, "seq1.pdb");
     pdbFiles[1] = new PDBEntry("PDB2", "B", Type.PDB, "seq2.pdb");
     StructureSelectionManager ssm = new StructureSelectionManager();
-  
+
     /*
      * map residues 1-10 to residues 21-30 (atoms 105-150) in structures
      */
@@ -419,7 +423,7 @@ public class AAStructureBindingModelTest
     Map<Object, AtomSpecModel> colours = binding.buildColoursMap(ssm, seqs,
             af.alignPanel);
     ChimeraCommands helper = new ChimeraCommands();
-    
+
     /*
      * M colour is #82827d (see strand.html help page)
      * sequence residue 1 mapped to structure residue 21
@@ -452,7 +456,8 @@ public class AAStructureBindingModelTest
     Color gray = new Color(128, 128, 128);
     atomSpec = colours.get(gray);
     assertNotNull(atomSpec);
-    assertEquals(helper.getAtomSpec(atomSpec, false), "#0:23-25.A|#1:23-25.B");
+    assertEquals(helper.getAtomSpec(atomSpec, false),
+            "#0:23-25.A|#1:23-25.B");
 
     /*
      * S and G are both coloured #4949b6, structure residues 26-30
@@ -463,4 +468,96 @@ public class AAStructureBindingModelTest
     assertEquals(helper.getAtomSpec(atomSpec, false),
             "#0:26-30.A|#1:26-30.B");
   }
+
+  @Test(groups = { "Functional" })
+  public void testGetShownResidues()
+  {
+    /*
+     * load these sequences, with columns 2-4 (third, fourth, fifth) hidden
+     */
+    String fasta = ">seq1/12-21\nMHRSQSSSGG\n>seq2/12-21\nMVRSNGGSSS";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fasta,
+            DataSourceType.PASTE);
+    AlignmentI al = af.getViewport().getAlignment();
+    ColumnSelection cs = new ColumnSelection();
+    cs.addElement(2);
+    cs.addElement(3);
+    cs.addElement(4);
+    af.getViewport().setColumnSelection(cs);
+    af.hideSelColumns_actionPerformed(null);
+    SequenceI seq1 = al.getSequenceAt(0);
+    SequenceI seq2 = al.getSequenceAt(1);
+    SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
+    PDBEntry[] pdbFiles = new PDBEntry[2];
+    pdbFiles[0] = new PDBEntry("PDB1", "A", Type.PDB, "seq1.pdb");
+    pdbFiles[1] = new PDBEntry("PDB2", "B", Type.PDB, "seq2.pdb");
+    StructureSelectionManager ssm = new StructureSelectionManager();
+
+    /*
+     * map residues 12-21 to residues 21-30 (atoms 105-150) in structures
+     */
+    HashMap<Integer, int[]> map = new HashMap<>();
+    for (int pos = 1; pos <= seq1.getLength(); pos++)
+    {
+      map.put(pos + 11, new int[] { 20 + pos, 5 * (20 + pos) });
+    }
+    StructureMapping sm1 = new StructureMapping(seq1, "seq1.pdb", "pdb1",
+            "A", map, null);
+    ssm.addStructureMapping(sm1);
+    StructureMapping sm2 = new StructureMapping(seq2, "seq2.pdb", "pdb2",
+            "B", map, null);
+    ssm.addStructureMapping(sm2);
+
+    AAStructureBindingModel binding = newBindingModel(pdbFiles, seqs, ssm,
+            af.alignPanel);
+
+    /*
+     * with hidden columns shown on structure
+     */
+    binding.setShowAlignmentOnly(true);
+    AtomSpecModel model = binding.getShownResidues(af.getViewport());
+    assertEquals(model.getModelCount(), 2);
+    int modelNo = 0;
+    for (String modelId : model.getModels())
+    {
+      assertEquals(modelId, String.valueOf(modelNo));
+      Iterable<String> chains = model.getChains(modelId);
+      String expectedChain = (modelNo == 0) ? "A" : "B";
+      for (String chain : chains)
+      {
+        assertEquals(chain, expectedChain);
+        List<int[]> ranges = model.getRanges(modelId, chain);
+        assertEquals(ranges.size(), 1);
+        assertEquals(ranges.get(0)[0], 21);
+        assertEquals(ranges.get(0)[1], 30);
+      }
+      modelNo++;
+    }
+
+    /*
+     * with hidden columns hidden on structure
+     * columns 2-4 correspond to residues 23-25 on structure - now omitted
+     */
+    binding.setHideHiddenRegions(true);
+    model = binding.getShownResidues(af.getViewport());
+    assertEquals(model.getModelCount(), 2);
+    modelNo = 0;
+    for (String modelId : model.getModels())
+    {
+      assertEquals(modelId, String.valueOf(modelNo));
+      Iterable<String> chains = model.getChains(modelId);
+      String expectedChain = (modelNo == 0) ? "A" : "B";
+      for (String chain : chains)
+      {
+        assertEquals(chain, expectedChain);
+        List<int[]> ranges = model.getRanges(modelId, chain);
+        assertEquals(ranges.size(), 2);
+        assertEquals(ranges.get(0)[0], 21);
+        assertEquals(ranges.get(0)[1], 22);
+        assertEquals(ranges.get(1)[0], 26);
+        assertEquals(ranges.get(1)[1], 30);
+      }
+      modelNo++;
+    }
+  }
 }
\ No newline at end of file