JAL-3551 copy Jalview features to Pymol 'p' (with pull refactoring)
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 21 May 2020 17:27:28 +0000 (18:27 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 21 May 2020 17:27:28 +0000 (18:27 +0100)
21 files changed:
resources/lang/Messages.properties
resources/lang/Messages_es.properties
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/AppJmol.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/ChimeraXViewFrame.java
src/jalview/gui/PymolBindingModel.java
src/jalview/gui/PymolViewer.java
src/jalview/gui/StructureViewerBase.java
src/jalview/structure/StructureCommandsBase.java
src/jalview/structure/StructureCommandsI.java
src/jalview/structures/models/AAStructureBindingModel.java
test/jalview/ext/pymol/PymolCommandsTest.java
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java
test/jalview/ext/rbvi/chimera/ChimeraXCommandsTest.java
test/jalview/structures/models/AAStructureBindingModelTest.java

index 3ba0649..ca6ae63 100644 (file)
@@ -502,8 +502,7 @@ label.insert_gaps = Insert {0} gaps
 label.delete_gap = Delete 1 gap
 label.delete_gaps = Delete {0} gaps
 label.sequence_details = Sequence Details
-label.jmol_help = Jmol Help
-label.chimera_help = Chimera Help
+label.viewer_help = {0} Help
 label.close_viewer = Close Viewer
 label.confirm_close_viewer = This will close Jalview''s connection to {0}.<br>Do you want to close the {1} window as well?
 label.all = All
@@ -715,10 +714,8 @@ label.colour_with_viewer = Colour in structure viewer
 label.superpose_structures = Superpose Structures
 error.superposition_failed = Superposition failed: {0}
 label.insufficient_residues = Not enough aligned residues ({0}) to perform superposition
-label.jmol = Jmol
-label.chimera = Chimera
-label.create_chimera_attributes = Write Jalview features
-label.create_chimera_attributes_tip = Set Chimera residue attributes for visible features
+label.create_viewer_attributes = Write Jalview features
+label.create_viewer_attributes_tip = Set structure residue attributes for Jalview features
 label.attributes_set = {0} attribute values set on Chimera
 label.sort_alignment_by_tree = Sort Alignment By Tree
 label.mark_unlinked_leaves = Mark Unlinked Leaves
index 1d9e5fd..624e619 100644 (file)
@@ -465,7 +465,7 @@ label.insert_gaps = Insertar {0} huecos
 label.delete_gap = Borrar 1 hueco
 label.delete_gaps = Borrar {0} huecos
 label.sequence_details = Detalles de la secuencia
-label.jmol_help = Ayuda de Jmol
+label.viewer_help = Ayuda sobre {0}
 # Todos/Todas is gender-sensitive, but currently only used for feminine (cadena / anotación)! 
 label.all = Todas
 label.sort_by = Ordenar por
@@ -653,7 +653,6 @@ label.associate_nodes_with = Asociar nodos con
 label.link_name = Nombre del enalce
 label.pdb_file = Fichero PDB
 label.colour_with_jmol = Colorear con Jmol
-label.jmol = Jmol
 label.sort_alignment_by_tree = Ordenar alineamiento por árbol
 label.mark_unlinked_leaves = Marcar las hojas como no enlazadas
 label.associate_leaves_with = Asociar hojas con
@@ -1118,7 +1117,6 @@ label.autoadd_secstr=A
 action.annotations=Anotaciones
 label.nuc_alignment_colour=Color del Alineamiento Nucleotídico
 label.copy_format_from=Copiar formato de
-label.chimera=Chimera
 label.create_chimera_attributes = Escribir características de Jalview
 label.create_chimera_attributes_tip = Establecer atributos en Chimera para características visibles 
 label.attributes_set = {0} valores de atributos establecidos en Chimera
@@ -1203,7 +1201,6 @@ label.viewer_missing=Visualizador de estructura no encontrado.<br/>Por favor, in
 warn.delete_all=<html>Borrar todas las secuencias cerrará la ventana del alineamiento.<br>Confirmar o Cancelar.
 label.select_all=Seleccionar Todos
 label.alpha_helix=Hélice Alfa
-label.chimera_help=Ayuda para Chimera
 label.find_tip=Buscar alineamiento, selección o IDs de secuencia para una subsecuencia (sin huecos)
 label.structure_viewer=Visualizador por defecto
 label.embbed_biojson=Incrustar BioJSON al exportar HTML
index 038ca48..0b37ceb 100644 (file)
@@ -39,7 +39,9 @@ import org.jmol.api.JmolViewer;
 import org.jmol.c.CBK;
 import org.jmol.viewer.Viewer;
 
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
+import jalview.api.SequenceRenderer;
 import jalview.bin.Cache;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
@@ -52,6 +54,7 @@ import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
+import javajs.util.BS;
 
 public abstract class JalviewJmolBinding extends AAStructureBindingModel
         implements JmolStatusListener, JmolSelectionListener,
@@ -997,4 +1000,23 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   {
     return ".spt";
   }
+
+  @Override
+  public void selectionChanged(BS arg0)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  @Override
+  public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
+  {
+    return new jalview.gui.SequenceRenderer(avp.getAlignViewport());
+  }
+
+  @Override
+  public String getHelpURL()
+  {
+    return "http://wiki.jmol.org"; // BH 2018
+  }
 }
index 3e0afd7..99130b4 100644 (file)
@@ -224,7 +224,7 @@ public class JmolCommands extends StructureCommandsBase
   }
 
   @Override
-  protected StructureCommandI getColourCommand(String atomSpec, Color colour)
+  protected StructureCommandI colourResidues(String atomSpec, Color colour)
   {
     StringBuilder sb = new StringBuilder(atomSpec.length()+20);
     sb.append("select ").append(atomSpec).append(getCommandSeparator())
index e4f9f5f..53b1ec5 100644 (file)
@@ -191,7 +191,7 @@ public class PymolCommands extends StructureCommandsBase
   }
 
   @Override
-  protected StructureCommandI getColourCommand(String atomSpec, Color colour)
+  protected StructureCommandI colourResidues(String atomSpec, Color colour)
   {
     // https://pymolwiki.org/index.php/Color
     return new StructureCommand("color", getColourString(colour), atomSpec);
@@ -233,4 +233,80 @@ public class PymolCommands extends StructureCommandsBase
     return commands;
   }
 
+  /**
+   * Returns a viewer command to set the given atom property value on atoms
+   * specified by the AtomSpecModel, for example
+   * 
+   * <pre>
+   * iterate 4zho//B/12-34,48-55/CA,jv_chain='primary'
+   * </pre>
+   * 
+   * @param attributeName
+   * @param attributeValue
+   * @param atomSpecModel
+   * @return
+   */
+  protected StructureCommandI setAttribute(String attributeName,
+          String attributeValue,
+          AtomSpecModel atomSpecModel)
+  {
+    StringBuilder sb = new StringBuilder(128);
+    sb.append("p.").append(attributeName).append("='")
+            .append(attributeValue).append("'");
+    String atomSpec = getAtomSpec(atomSpecModel, false);
+    return new StructureCommand("iterate", atomSpec, sb.toString());
+  }
+
+  /**
+   * Traverse the map of features/values/models/chains/positions to construct a
+   * list of 'set property' commands (one per distinct feature type and value).
+   * The values are stored in the 'p' dictionary of user-defined properties of
+   * each atom.
+   * <p>
+   * The format of each command is
+   * 
+   * <pre>
+   * <blockquote> iterate atomspec, p.featureName='value' 
+   * e.g. iterate 4zho//A/23,28-29/CA, p.jv_Metal='Fe'
+   * </blockquote>
+   * </pre>
+   * 
+   * @param featureMap
+   * @return
+   */
+  @Override
+  public List<StructureCommandI> setAttributes(
+          Map<String, Map<Object, AtomSpecModel>> featureMap)
+  {
+    List<StructureCommandI> commands = new ArrayList<>();
+    for (String featureType : featureMap.keySet())
+    {
+      String attributeName = makeAttributeName(featureType);
+  
+      /*
+       * todo: clear down existing attributes for this feature?
+       */
+      // commands.add(new StructureCommand("iterate", "all",
+      // "p."+attributeName+"='None'"); //?
+  
+      Map<Object, AtomSpecModel> values = featureMap.get(featureType);
+      for (Object value : values.keySet())
+      {
+        /*
+         * for each distinct value recorded for this feature type,
+         * add a command to set the attribute on the mapped residues
+         * Put values in single quotes, encoding any embedded single quotes
+         */
+        AtomSpecModel atomSpecModel = values.get(value);
+        String featureValue = value.toString();
+        featureValue = featureValue.replaceAll("\\'", "&#39;");
+        StructureCommandI cmd = setAttribute(attributeName, featureValue,
+                atomSpecModel);
+        commands.add(cmd);
+      }
+    }
+  
+    return commands;
+  }
+
 }
index c9dbc1d..ac10b0b 100644 (file)
@@ -23,25 +23,13 @@ package jalview.ext.rbvi.chimera;
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.api.FeatureRenderer;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.MappedFeatures;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.gui.Desktop;
 import jalview.structure.AtomSpecModel;
 import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
 import jalview.structure.StructureCommandsBase;
-import jalview.structure.StructureMapping;
-import jalview.structure.StructureSelectionManager;
 import jalview.util.ColorUtils;
 
 /**
@@ -55,8 +43,6 @@ public class ChimeraCommands extends StructureCommandsBase
   private static final StructureCommand SHOW_BACKBONE = new StructureCommand(
           "~display all;~ribbon;chain @CA|P");
 
-  public static final String NAMESPACE_PREFIX = "jv_";
-
   private static final StructureCommandI COLOUR_BY_CHARGE = new StructureCommand(
           "color white;color red ::ASP,GLU;color blue ::LYS,ARG;color yellow ::CYS");
 
@@ -67,7 +53,7 @@ public class ChimeraCommands extends StructureCommandsBase
   private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
 
   @Override
-  public StructureCommandI getColourCommand(String atomSpec, Color colour)
+  public StructureCommandI colourResidues(String atomSpec, Color colour)
   {
     // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/color.html
     String colourCode = getColourString(colour);
@@ -86,256 +72,6 @@ public class ChimeraCommands extends StructureCommandsBase
   }
 
   /**
-   * Constructs and returns Chimera commands to set attributes on residues
-   * corresponding to features in Jalview. Attribute names are the Jalview feature
-   * type, with a "jv_" prefix.
-   * 
-   * @param ssm
-   * @param files
-   * @param seqs
-   * @param viewPanel
-   * @return
-   */
-  @Override
-  public List<StructureCommandI> setAttributesForFeatures(
-          StructureSelectionManager ssm, String[] files, SequenceI[][] seqs,
-          AlignmentViewPanel viewPanel)
-  {
-    Map<String, Map<Object, AtomSpecModel>> featureMap = buildFeaturesMap(
-            ssm, files, seqs, viewPanel);
-
-    return setAttributes(featureMap);
-  }
-
-  /**
-   * <pre>
-   * Helper method to build a map of 
-   *   { featureType, { feature value, AtomSpecModel } }
-   * </pre>
-   * 
-   * @param ssm
-   * @param files
-   * @param seqs
-   * @param viewPanel
-   * @return
-   */
-  protected Map<String, Map<Object, AtomSpecModel>> buildFeaturesMap(
-          StructureSelectionManager ssm, String[] files, SequenceI[][] seqs,
-          AlignmentViewPanel viewPanel)
-  {
-    Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<>();
-
-    FeatureRenderer fr = viewPanel.getFeatureRenderer();
-    if (fr == null)
-    {
-      return theMap;
-    }
-
-    AlignViewportI viewport = viewPanel.getAlignViewport();
-    List<String> visibleFeatures = fr.getDisplayedFeatureTypes();
-
-    /*
-     * if alignment is showing features from complement, we also transfer
-     * these features to the corresponding mapped structure residues
-     */
-    boolean showLinkedFeatures = viewport.isShowComplementFeatures();
-    List<String> complementFeatures = new ArrayList<>();
-    FeatureRenderer complementRenderer = null;
-    if (showLinkedFeatures)
-    {
-      AlignViewportI comp = fr.getViewport().getCodingComplement();
-      if (comp != null)
-      {
-        complementRenderer = Desktop.getAlignFrameFor(comp)
-                .getFeatureRenderer();
-        complementFeatures = complementRenderer.getDisplayedFeatureTypes();
-      }
-    }
-    if (visibleFeatures.isEmpty() && complementFeatures.isEmpty())
-    {
-      return theMap;
-    }
-
-    AlignmentI alignment = viewPanel.getAlignment();
-    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
-    {
-      final int modelNumber = pdbfnum + getModelStartNo();
-      String modelId = String.valueOf(modelNumber);
-      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
-
-      if (mapping == null || mapping.length < 1)
-      {
-        continue;
-      }
-
-      for (int seqNo = 0; seqNo < seqs[pdbfnum].length; seqNo++)
-      {
-        for (int m = 0; m < mapping.length; m++)
-        {
-          final SequenceI seq = seqs[pdbfnum][seqNo];
-          int sp = alignment.findIndex(seq);
-          StructureMapping structureMapping = mapping[m];
-          if (structureMapping.getSequence() == seq && sp > -1)
-          {
-            /*
-             * found a sequence with a mapping to a structure;
-             * now scan its features
-             */
-            if (!visibleFeatures.isEmpty())
-            {
-              scanSequenceFeatures(visibleFeatures, structureMapping, seq,
-                      theMap, modelId);
-            }
-            if (showLinkedFeatures)
-            {
-              scanComplementFeatures(complementRenderer, structureMapping,
-                      seq, theMap, modelId);
-            }
-          }
-        }
-      }
-    }
-    return theMap;
-  }
-
-  /**
-   * Scans visible features in mapped positions of the CDS/peptide complement, and
-   * adds any found to the map of attribute values/structure positions
-   * 
-   * @param complementRenderer
-   * @param structureMapping
-   * @param seq
-   * @param theMap
-   * @param modelNumber
-   */
-  protected static void scanComplementFeatures(
-          FeatureRenderer complementRenderer,
-          StructureMapping structureMapping, SequenceI seq,
-          Map<String, Map<Object, AtomSpecModel>> theMap,
-          String modelNumber)
-  {
-    /*
-     * for each sequence residue mapped to a structure position...
-     */
-    for (int seqPos : structureMapping.getMapping().keySet())
-    {
-      /*
-       * find visible complementary features at mapped position(s)
-       */
-      MappedFeatures mf = complementRenderer
-              .findComplementFeaturesAtResidue(seq, seqPos);
-      if (mf != null)
-      {
-        for (SequenceFeature sf : mf.features)
-        {
-          String type = sf.getType();
-
-          /*
-           * Don't copy features which originated from Chimera
-           */
-          if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
-                  .equals(sf.getFeatureGroup()))
-          {
-            continue;
-          }
-
-          /*
-           * record feature 'value' (score/description/type) as at the
-           * corresponding structure position
-           */
-          List<int[]> mappedRanges = structureMapping
-                  .getPDBResNumRanges(seqPos, seqPos);
-
-          if (!mappedRanges.isEmpty())
-          {
-            String value = sf.getDescription();
-            if (value == null || value.length() == 0)
-            {
-              value = type;
-            }
-            float score = sf.getScore();
-            if (score != 0f && !Float.isNaN(score))
-            {
-              value = Float.toString(score);
-            }
-            Map<Object, AtomSpecModel> featureValues = theMap.get(type);
-            if (featureValues == null)
-            {
-              featureValues = new HashMap<>();
-              theMap.put(type, featureValues);
-            }
-            for (int[] range : mappedRanges)
-            {
-              addAtomSpecRange(featureValues, value, modelNumber, range[0],
-                      range[1], structureMapping.getChain());
-            }
-          }
-        }
-      }
-    }
-  }
-
-  /**
-   * Inspect features on the sequence; for each feature that is visible,
-   * determine its mapped ranges in the structure (if any) according to the
-   * given mapping, and add them to the map.
-   * 
-   * @param visibleFeatures
-   * @param mapping
-   * @param seq
-   * @param theMap
-   * @param modelId
-   */
-  protected static void scanSequenceFeatures(List<String> visibleFeatures,
-          StructureMapping mapping, SequenceI seq,
-          Map<String, Map<Object, AtomSpecModel>> theMap, String modelId)
-  {
-    List<SequenceFeature> sfs = seq.getFeatures().getPositionalFeatures(
-            visibleFeatures.toArray(new String[visibleFeatures.size()]));
-    for (SequenceFeature sf : sfs)
-    {
-      String type = sf.getType();
-
-      /*
-       * Don't copy features which originated from Chimera
-       */
-      if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
-              .equals(sf.getFeatureGroup()))
-      {
-        continue;
-      }
-
-      List<int[]> mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(),
-              sf.getEnd());
-
-      if (!mappedRanges.isEmpty())
-      {
-        String value = sf.getDescription();
-        if (value == null || value.length() == 0)
-        {
-          value = type;
-        }
-        float score = sf.getScore();
-        if (score != 0f && !Float.isNaN(score))
-        {
-          value = Float.toString(score);
-        }
-        Map<Object, AtomSpecModel> featureValues = theMap.get(type);
-        if (featureValues == null)
-        {
-          featureValues = new HashMap<>();
-          theMap.put(type, featureValues);
-        }
-        for (int[] range : mappedRanges)
-        {
-          addAtomSpecRange(featureValues, value, modelId, range[0],
-                  range[1], mapping.getChain());
-        }
-      }
-    }
-  }
-
-  /**
    * Traverse the map of features/values/models/chains/positions to construct a
    * list of 'setattr' commands (one per distinct feature type and value).
    * <p>
@@ -350,7 +86,8 @@ public class ChimeraCommands extends StructureCommandsBase
    * @param featureMap
    * @return
    */
-  protected List<StructureCommandI> setAttributes(
+  @Override
+  public List<StructureCommandI> setAttributes(
           Map<String, Map<Object, AtomSpecModel>> featureMap)
   {
     List<StructureCommandI> commands = new ArrayList<>();
@@ -417,17 +154,9 @@ public class ChimeraCommands extends StructureCommandsBase
    * @return
    * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/setattr.html
    */
-  protected static String makeAttributeName(String featureType)
+  protected String makeAttributeName(String featureType)
   {
-    StringBuilder sb = new StringBuilder();
-    if (featureType != null)
-    {
-      for (char c : featureType.toCharArray())
-      {
-        sb.append(Character.isLetterOrDigit(c) ? c : '_');
-      }
-    }
-    String attName = NAMESPACE_PREFIX + sb.toString();
+    String attName = super.makeAttributeName(featureType);
 
     /*
      * Chimera treats an attribute name ending in 'color' as colour-valued;
index 3341199..b7d9ce3 100644 (file)
@@ -63,7 +63,7 @@ public class ChimeraXCommands extends ChimeraCommands
   }
 
   @Override
-  public StructureCommandI getColourCommand(String atomSpec, Color colour)
+  public StructureCommandI colourResidues(String atomSpec, Color colour)
   {
     // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/color.html
     String colourCode = getColourString(colour);
index 98cc1ff..d1b8583 100644 (file)
@@ -38,6 +38,7 @@ import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResultMatchI;
@@ -48,6 +49,7 @@ import jalview.gui.StructureViewer.ViewerType;
 import jalview.httpserver.AbstractRequestHandler;
 import jalview.io.DataSourceType;
 import jalview.structure.AtomSpec;
+import jalview.structure.AtomSpecModel;
 import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
 import jalview.structure.StructureSelectionManager;
@@ -59,15 +61,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   public static final String CHIMERA_FEATURE_GROUP = "Chimera";
 
-  // Chimera clause to exclude alternate locations in atom selection
-  private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
-
-  private static final boolean debug = false;
-
-  private static final String PHOSPHORUS = "P";
-
-  private static final String ALPHACARBON = "CA";
-
   /*
    * Object through which we talk to Chimera
    */
@@ -367,10 +360,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     if (getResponse)
     {
       reply = lastReply;
-      if (debug)
-      {
-        log("Response from command ('" + cmd + "') was:\n" + lastReply);
-      }
+      Cache.log.debug(
+              "Response from command ('" + cmd + "') was:\n" + lastReply);
     }
 
     return reply;
@@ -593,7 +584,11 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   /**
    * Constructs and send commands to Chimera to set attributes on residues for
-   * features visible in Jalview
+   * features visible in Jalview.
+   * <p>
+   * The syntax is: setattr r &lt;attName&gt; &lt;attValue&gt; &lt;atomSpec&gt;
+   * <p>
+   * For example: setattr r jv_chain "Ferredoxin-1, Chloroplastic" #0:94.A
    * 
    * @param avp
    * @return
@@ -601,14 +596,11 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   public int sendFeaturesToViewer(AlignmentViewPanel avp)
   {
     // TODO refactor as required to pull up to an interface
-    String[] files = getStructureFiles();
-    if (files == null)
-    {
-      return 0;
-    }
 
+    Map<String, Map<Object, AtomSpecModel>> featureValues = buildFeaturesMap(
+            avp);
     List<StructureCommandI> commands = getCommandGenerator()
-            .setAttributesForFeatures(getSsm(), files, getSequence(), avp);
+            .setAttributes(featureValues);
     if (commands.size() > 10)
     {
       sendCommandsByFile(commands);
@@ -844,6 +836,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     return CHIMERA_SESSION_EXTENSION;
   }
 
+  @Override
   public String getHelpURL()
   {
     return "https://www.cgl.ucsf.edu/chimera/docs/UsersGuide";
index ffc8053..87fb1b4 100644 (file)
@@ -147,8 +147,6 @@ public class AppJmol extends StructureViewerBase
   {
     super.initMenus();
 
-    viewerActionMenu.setText(MessageManager.getString("label.jmol"));
-
     viewerColour
             .setText(MessageManager.getString("label.colour_with_jmol"));
     viewerColour.setToolTipText(MessageManager
index 45ab9a9..65b002b 100644 (file)
@@ -25,7 +25,6 @@ import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -46,7 +45,6 @@ import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
 import jalview.structures.models.AAStructureBindingModel;
-import jalview.util.BrowserLauncher;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
@@ -80,16 +78,13 @@ public class ChimeraViewFrame extends StructureViewerBase
   {
     super.initMenus();
 
-    viewerActionMenu.setText(MessageManager.getString("label.chimera"));
-
-    helpItem.setText(MessageManager.getString("label.chimera_help"));
     savemenu.setVisible(false); // not yet implemented
     viewMenu.add(fitToWindow);
 
     JMenuItem writeFeatures = new JMenuItem(
-            MessageManager.getString("label.create_chimera_attributes"));
+            MessageManager.getString("label.create_viewer_attributes"));
     writeFeatures.setToolTipText(MessageManager
-            .getString("label.create_chimera_attributes_tip"));
+            .getString("label.create_viewer_attributes_tip"));
     writeFeatures.addActionListener(new ActionListener()
     {
       @Override
@@ -154,14 +149,12 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * Send a command to Chimera to create residue attributes for Jalview features
-   * <p>
-   * The syntax is: setattr r &lt;attName&gt; &lt;attValue&gt; &lt;atomSpec&gt;
-   * <p>
-   * For example: setattr r jv_chain "Ferredoxin-1, Chloroplastic" #0:94.A
+   * Sends command(s) to the structure viewer to create residue attributes for
+   * visible Jalview features
    */
   protected void sendFeaturesToChimera()
   {
+    // todo pull up?
     int count = jmb.sendFeaturesToViewer(getAlignmentPanel());
     statusBar.setText(
             MessageManager.formatMessage("label.attributes_set", count));
@@ -529,20 +522,6 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   @Override
-  public void showHelp_actionPerformed()
-  {
-    try
-    {
-      String url = jmb.getHelpURL();
-      BrowserLauncher.openURL(url);
-    } catch (IOException ex)
-    {
-      System.err
-              .println("Show Chimera help failed with: " + ex.getMessage());
-    }
-  }
-
-  @Override
   public AAStructureBindingModel getBinding()
   {
     return jmb;
index d0353f3..a823235 100644 (file)
@@ -66,13 +66,4 @@ public class ChimeraXViewFrame extends ChimeraViewFrame
     return new JalviewChimeraXBindingModel(this,
             ap.getStructureSelectionManager(), pdbentrys, seqs, null);
   }
-
-  @Override
-  protected void initMenus()
-  {
-    super.initMenus();
-
-    viewerActionMenu.setText("ChimeraX");
-  }
-
 }
index fc957bb..21ba95c 100644 (file)
@@ -12,6 +12,7 @@ import jalview.ext.pymol.PymolCommands;
 import jalview.ext.pymol.PymolManager;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.structure.AtomSpec;
+import jalview.structure.AtomSpecModel;
 import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
 import jalview.structure.StructureSelectionManager;
@@ -103,10 +104,9 @@ public class PymolBindingModel extends AAStructureBindingModel
   }
 
   @Override
-  public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment)
+  public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
   {
-    // pull up?
-    return new SequenceRenderer(alignment.getAlignViewport());
+    return new SequenceRenderer(avp.getAlignViewport());
   }
 
   @Override
@@ -222,4 +222,28 @@ public class PymolBindingModel extends AAStructureBindingModel
     return ".pse";
   }
 
+  @Override
+  public String getHelpURL()
+  {
+    return "https://pymolwiki.org/";
+  }
+
+  /**
+   * Constructs and sends commands to set atom properties for visible Jalview
+   * features on residues mapped to structure
+   * 
+   * @param avp
+   * @return
+   */
+  public int sendFeaturesToViewer(AlignmentViewPanel avp)
+  {
+    // todo pull up this and JalviewChimeraBinding variant
+    Map<String, Map<Object, AtomSpecModel>> featureValues = buildFeaturesMap(
+            avp);
+    List<StructureCommandI> commands = getCommandGenerator()
+            .setAttributes(featureValues);
+    executeCommands(commands, false, null);
+    return commands.size();
+  }
+
 }
index 98582b2..4e0ac95 100644 (file)
@@ -1,10 +1,13 @@
 package jalview.gui;
 
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
 import javax.swing.JInternalFrame;
+import javax.swing.JMenuItem;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
@@ -328,4 +331,34 @@ public class PymolViewer extends StructureViewerBase
     return "PyMOL";
   }
 
+  @Override
+  protected void initMenus()
+  {
+    super.initMenus();
+
+    savemenu.setVisible(false); // not yet implemented
+    viewMenu.add(fitToWindow);
+
+    JMenuItem writeFeatures = new JMenuItem(
+            MessageManager.getString("label.create_viewer_attributes"));
+    writeFeatures.setToolTipText(MessageManager
+            .getString("label.create_viewer_attributes_tip"));
+    writeFeatures.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        sendFeaturesToPymol();
+      }
+    });
+    viewerActionMenu.add(writeFeatures);
+  }
+
+  protected void sendFeaturesToPymol()
+  {
+    int count = binding.sendFeaturesToViewer(getAlignmentPanel());
+    statusBar.setText(
+            MessageManager.formatMessage("label.attributes_set", count));
+  }
+
 }
index e180f6b..fccd9bf 100644 (file)
@@ -61,6 +61,7 @@ import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemes;
 import jalview.structure.StructureMapping;
 import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.BrowserLauncher;
 import jalview.util.MessageManager;
 import jalview.ws.dbsources.Pdb;
 
@@ -706,7 +707,7 @@ public abstract class StructureViewerBase extends GStructureViewer
                 }
                 else
                 {
-                  // update the Chimera display now.
+                  // update the viewer display now.
                   seqColour_actionPerformed();
                 }
               }
@@ -756,6 +757,10 @@ public abstract class StructureViewerBase extends GStructureViewer
       }
     });
 
+    viewerActionMenu.setText(getViewerName());
+    helpItem.setText(MessageManager.formatMessage("label.viewer_help",
+            getViewerName()));
+
     buildColourMenu();
   }
 
@@ -1216,4 +1221,22 @@ public abstract class StructureViewerBase extends GStructureViewer
     dispose();
   }
 
+  @Override
+  public void showHelp_actionPerformed()
+  {
+    try
+    {
+      String url = getBinding().getHelpURL();
+      if (url != null)
+      {
+        BrowserLauncher.openURL(url);
+      }
+    } catch (IOException ex)
+    {
+      System.err
+              .println("Show " + getViewerName() + " failed with: "
+                      + ex.getMessage());
+    }
+  }
+
 }
index e688b64..3c29fd4 100644 (file)
@@ -6,9 +6,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import jalview.api.AlignmentViewPanel;
-import jalview.datamodel.SequenceI;
-
 /**
  * A base class holding methods useful to all classes that implement commands
  * for structure viewers
@@ -19,6 +16,7 @@ import jalview.datamodel.SequenceI;
 public abstract class StructureCommandsBase implements StructureCommandsI
 {
   private static final String CMD_SEPARATOR = ";";
+  public static final String NAMESPACE_PREFIX = "jv_";
 
   /**
    * Returns something that separates concatenated commands
@@ -30,15 +28,6 @@ public abstract class StructureCommandsBase implements StructureCommandsI
     return CMD_SEPARATOR;
   }
 
-  @Override
-  public List<StructureCommandI> setAttributesForFeatures(
-          StructureSelectionManager ssm,
-          String[] files, SequenceI[][] sequence, AlignmentViewPanel avp)
-  {
-    // default does nothing, override where this is implemented
-    return null;
-  }
-
   /**
    * Returns the lowest model number used by the structure viewer
    * 
@@ -84,6 +73,28 @@ public abstract class StructureCommandsBase implements StructureCommandsI
   }
 
   /**
+   * Makes a structure viewer attribute name for a Jalview feature type by
+   * prefixing it with "jv_", and replacing any non-alphanumeric characters with
+   * an underscore
+   * 
+   * @param featureType
+   * @return
+   */
+  protected String makeAttributeName(String featureType)
+  {
+    StringBuilder sb = new StringBuilder();
+    if (featureType != null)
+    {
+      for (char c : featureType.toCharArray())
+      {
+        sb.append(Character.isLetterOrDigit(c) ? c : '_');
+      }
+    }
+    String attName = NAMESPACE_PREFIX + sb.toString();
+    return attName;
+  }
+
+  /**
    * Traverse the map of colours/models/chains/positions to construct a list of
    * 'color' commands (one per distinct colour used). The format of each command
    * is specific to the structure viewer.
@@ -130,7 +141,7 @@ public abstract class StructureCommandsBase implements StructureCommandsI
           Color colour)
   {
     String atomSpec = getAtomSpec(atomSpecModel, false);
-    return getColourCommand(atomSpec, colour);
+    return colourResidues(atomSpec, colour);
   }
 
   /**
@@ -141,7 +152,7 @@ public abstract class StructureCommandsBase implements StructureCommandsI
    * @param colour
    * @return
    */
-  protected abstract StructureCommandI getColourCommand(String atomSpec,
+  protected abstract StructureCommandI colourResidues(String atomSpec,
           Color colour);
 
   @Override
@@ -159,7 +170,7 @@ public abstract class StructureCommandsBase implements StructureCommandsI
   private StructureCommandI colourResidue(String resName, Color col)
   {
     String atomSpec = getResidueSpec(resName);
-    return getColourCommand(atomSpec, col);
+    return colourResidues(atomSpec, col);
   }
 
   /**
@@ -204,4 +215,12 @@ public abstract class StructureCommandsBase implements StructureCommandsI
    * @return
    */
   protected abstract String getResidueSpec(String residue);
+
+  @Override
+  public List<StructureCommandI> setAttributes(
+          Map<String, Map<Object, AtomSpecModel>> featureValues)
+  {
+    // default does nothing, override where this is implemented
+    return null;
+  }
 }
index 8725b3d..871d84b 100644 (file)
@@ -1,12 +1,12 @@
 package jalview.structure;
 
-import jalview.api.AlignmentViewPanel;
-import jalview.datamodel.SequenceI;
-
 import java.awt.Color;
 import java.util.List;
 import java.util.Map;
 
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.SequenceI;
+
 /**
  * Methods that generate commands that can be sent to a molecular structure
  * viewer program (e.g. Jmol, Chimera, ChimeraX)
@@ -82,20 +82,6 @@ public interface StructureCommandsI
   List<StructureCommandI> showChains(List<String> toShow);
 
   /**
-   * Returns zero, one or more commands to set attributes on mapped residues in
-   * the structure viewer for any features present and displayed in Jalview
-   * 
-   * @param ssm
-   * @param files
-   * @param sequence
-   * @param avp
-   * @return
-   */
-  List<StructureCommandI> setAttributesForFeatures(
-          StructureSelectionManager ssm,
-          String[] files, SequenceI[][] sequence, AlignmentViewPanel avp);
-
-  /**
    * 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
@@ -162,4 +148,16 @@ public interface StructureCommandsI
    */
   // refactor if needed to distinguish loading data or session files
   StructureCommandI loadFile(String file);
+
+  /**
+   * Returns commands to set atom attributes or properties, given a map of
+   * Jalview features as {featureType, {featureValue, AtomSpecModel}}. The
+   * assumption is that one command can be constructed for each feature type and
+   * value combination, to apply it to one or more residues.
+   * 
+   * @param featureValues
+   * @return
+   */
+  List<StructureCommandI> setAttributes(
+          Map<String, Map<Object, AtomSpecModel>> featureValues);
 }
index 6e926df..8aa1895 100644 (file)
@@ -42,8 +42,12 @@ import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.MappedFeatures;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
+import jalview.gui.Desktop;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
@@ -1111,7 +1115,8 @@ public abstract class AAStructureBindingModel
    * @param getReply
    * @param msg
    */
-  private List<String> executeCommands(List<StructureCommandI> commands,
+  protected List<String> executeCommands(
+          List<StructureCommandI> commands,
           boolean getReply, String msg)
   {
     return executeCommands(getReply, msg,
@@ -1160,25 +1165,17 @@ public abstract class AAStructureBindingModel
   }
 
   /**
-   * colour any structures associated with sequences in the given alignment
-   * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
-   * if colourBySequence is enabled.
+   * Colours any structures associated with sequences in the given alignment as
+   * coloured in the alignment view, provided colourBySequence is enabled
    */
   public void colourBySequence(AlignmentViewPanel alignmentv)
   {
-    if (!colourBySequence || !isLoadingFinished())
+    if (!colourBySequence || !isLoadingFinished() || getSsm() == null)
     {
       return;
     }
-    if (getSsm() == null)
-    {
-      return;
-    }
-    String[] files = getStructureFiles();
-
-    SequenceRenderer sr = getSequenceRenderer(alignmentv);
-    Map<Object, AtomSpecModel> colourMap = buildColoursMap(ssm, files,
-            sequence, sr, alignmentv);
+    Map<Object, AtomSpecModel> colourMap = buildColoursMap(ssm, sequence,
+            alignmentv);
 
     List<StructureCommandI> colourBySequenceCommands = commandGenerator
             .colourBySequence(colourMap);
@@ -1354,16 +1351,16 @@ public abstract class AAStructureBindingModel
    * models and chains)
    * 
    * @param ssm
-   * @param files
    * @param sequence
-   * @param sr
    * @param viewPanel
    * @return
    */
   protected Map<Object, AtomSpecModel> buildColoursMap(
-          StructureSelectionManager ssm, String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr, AlignmentViewPanel viewPanel)
+          StructureSelectionManager ssm, SequenceI[][] sequence,
+          AlignmentViewPanel viewPanel)
   {
+    String[] files = getStructureFiles();
+    SequenceRenderer sr = getSequenceRenderer(viewPanel);
     FeatureRenderer fr = viewPanel.getFeatureRenderer();
     FeatureColourFinder finder = new FeatureColourFinder(fr);
     AlignViewportI viewport = viewPanel.getAlignViewport();
@@ -1594,4 +1591,245 @@ public abstract class AAStructureBindingModel
     // add external viewer shutdown in overrides
     // todo - or can maybe pull up to here
   }
+
+  /**
+   * Returns the URL of a help page for the structure viewer, or null if none is
+   * known
+   * 
+   * @return
+   */
+  public String getHelpURL()
+  {
+    return null;
+  }
+
+  /**
+   * <pre>
+   * Helper method to build a map of 
+   *   { featureType, { feature value, AtomSpecModel } }
+   * </pre>
+   * 
+   * @param viewPanel
+   * @return
+   */
+  protected Map<String, Map<Object, AtomSpecModel>> buildFeaturesMap(
+          AlignmentViewPanel viewPanel)
+  {
+    Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<>();
+    String[] files = getStructureFiles();
+    if (files == null)
+    {
+      return theMap;
+    }
+
+    FeatureRenderer fr = viewPanel.getFeatureRenderer();
+    if (fr == null)
+    {
+      return theMap;
+    }
+  
+    AlignViewportI viewport = viewPanel.getAlignViewport();
+    List<String> visibleFeatures = fr.getDisplayedFeatureTypes();
+  
+    /*
+     * if alignment is showing features from complement, we also transfer
+     * these features to the corresponding mapped structure residues
+     */
+    boolean showLinkedFeatures = viewport.isShowComplementFeatures();
+    List<String> complementFeatures = new ArrayList<>();
+    FeatureRenderer complementRenderer = null;
+    if (showLinkedFeatures)
+    {
+      AlignViewportI comp = fr.getViewport().getCodingComplement();
+      if (comp != null)
+      {
+        complementRenderer = Desktop.getAlignFrameFor(comp)
+                .getFeatureRenderer();
+        complementFeatures = complementRenderer.getDisplayedFeatureTypes();
+      }
+    }
+    if (visibleFeatures.isEmpty() && complementFeatures.isEmpty())
+    {
+      return theMap;
+    }
+  
+    AlignmentI alignment = viewPanel.getAlignment();
+    SequenceI[][] seqs = getSequence();
+
+    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+    {
+      String modelId = getModelIdForFile(files[pdbfnum]);
+      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
+  
+      if (mapping == null || mapping.length < 1)
+      {
+        continue;
+      }
+  
+      for (int seqNo = 0; seqNo < seqs[pdbfnum].length; seqNo++)
+      {
+        for (int m = 0; m < mapping.length; m++)
+        {
+          final SequenceI seq = seqs[pdbfnum][seqNo];
+          int sp = alignment.findIndex(seq);
+          StructureMapping structureMapping = mapping[m];
+          if (structureMapping.getSequence() == seq && sp > -1)
+          {
+            /*
+             * found a sequence with a mapping to a structure;
+             * now scan its features
+             */
+            if (!visibleFeatures.isEmpty())
+            {
+              scanSequenceFeatures(visibleFeatures, structureMapping, seq,
+                      theMap, modelId);
+            }
+            if (showLinkedFeatures)
+            {
+              scanComplementFeatures(complementRenderer, structureMapping,
+                      seq, theMap, modelId);
+            }
+          }
+        }
+      }
+    }
+    return theMap;
+  }
+
+  /**
+   * Scans visible features in mapped positions of the CDS/peptide complement, and
+   * adds any found to the map of attribute values/structure positions
+   * 
+   * @param complementRenderer
+   * @param structureMapping
+   * @param seq
+   * @param theMap
+   * @param modelNumber
+   */
+  protected static void scanComplementFeatures(
+          FeatureRenderer complementRenderer,
+          StructureMapping structureMapping, SequenceI seq,
+          Map<String, Map<Object, AtomSpecModel>> theMap,
+          String modelNumber)
+  {
+    /*
+     * for each sequence residue mapped to a structure position...
+     */
+    for (int seqPos : structureMapping.getMapping().keySet())
+    {
+      /*
+       * find visible complementary features at mapped position(s)
+       */
+      MappedFeatures mf = complementRenderer
+              .findComplementFeaturesAtResidue(seq, seqPos);
+      if (mf != null)
+      {
+        for (SequenceFeature sf : mf.features)
+        {
+          String type = sf.getType();
+  
+          /*
+           * Don't copy features which originated from Chimera
+           */
+          if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
+                  .equals(sf.getFeatureGroup()))
+          {
+            continue;
+          }
+  
+          /*
+           * record feature 'value' (score/description/type) as at the
+           * corresponding structure position
+           */
+          List<int[]> mappedRanges = structureMapping
+                  .getPDBResNumRanges(seqPos, seqPos);
+  
+          if (!mappedRanges.isEmpty())
+          {
+            String value = sf.getDescription();
+            if (value == null || value.length() == 0)
+            {
+              value = type;
+            }
+            float score = sf.getScore();
+            if (score != 0f && !Float.isNaN(score))
+            {
+              value = Float.toString(score);
+            }
+            Map<Object, AtomSpecModel> featureValues = theMap.get(type);
+            if (featureValues == null)
+            {
+              featureValues = new HashMap<>();
+              theMap.put(type, featureValues);
+            }
+            for (int[] range : mappedRanges)
+            {
+              addAtomSpecRange(featureValues, value, modelNumber, range[0],
+                      range[1], structureMapping.getChain());
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Inspect features on the sequence; for each feature that is visible,
+   * determine its mapped ranges in the structure (if any) according to the
+   * given mapping, and add them to the map.
+   * 
+   * @param visibleFeatures
+   * @param mapping
+   * @param seq
+   * @param theMap
+   * @param modelId
+   */
+  protected static void scanSequenceFeatures(List<String> visibleFeatures,
+          StructureMapping mapping, SequenceI seq,
+          Map<String, Map<Object, AtomSpecModel>> theMap, String modelId)
+  {
+    List<SequenceFeature> sfs = seq.getFeatures().getPositionalFeatures(
+            visibleFeatures.toArray(new String[visibleFeatures.size()]));
+    for (SequenceFeature sf : sfs)
+    {
+      String type = sf.getType();
+  
+      /*
+       * Don't copy features which originated from Chimera
+       */
+      if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
+              .equals(sf.getFeatureGroup()))
+      {
+        continue;
+      }
+  
+      List<int[]> mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(),
+              sf.getEnd());
+  
+      if (!mappedRanges.isEmpty())
+      {
+        String value = sf.getDescription();
+        if (value == null || value.length() == 0)
+        {
+          value = type;
+        }
+        float score = sf.getScore();
+        if (score != 0f && !Float.isNaN(score))
+        {
+          value = Float.toString(score);
+        }
+        Map<Object, AtomSpecModel> featureValues = theMap.get(type);
+        if (featureValues == null)
+        {
+          featureValues = new HashMap<>();
+          theMap.put(type, featureValues);
+        }
+        for (int[] range : mappedRanges)
+        {
+          addAtomSpecRange(featureValues, value, modelId, range[0],
+                  range[1], mapping.getChain());
+        }
+      }
+    }
+  }
 }
index c0f22c8..38031b6 100644 (file)
@@ -225,7 +225,7 @@ public class PymolCommandsTest
   {
     PymolCommands testee = new PymolCommands();
     assertEquals(
-            testee.getColourCommand("something", Color.MAGENTA).toString(),
+            testee.colourResidues("something", Color.MAGENTA).toString(),
             "color(0xff00ff,something)");
   }
 }
index f087020..5fc9fdc 100644 (file)
@@ -135,21 +135,24 @@ public class ChimeraCommandsTest
 
   /**
    * Tests for the method that prefixes and sanitises a feature name so it can
-   * be used as a valid, namespaced attribute name in Chimera
+   * be used as a valid, namespaced attribute name in Chimera or PyMol
    */
   @Test(groups = { "Functional" })
   public void testMakeAttributeName()
   {
-    assertEquals(ChimeraCommands.makeAttributeName(null), "jv_");
-    assertEquals(ChimeraCommands.makeAttributeName(""), "jv_");
-    assertEquals(ChimeraCommands.makeAttributeName("helix"), "jv_helix");
-    assertEquals(ChimeraCommands.makeAttributeName("Hello World 24"),
+    ChimeraCommands testee = new ChimeraCommands();
+    assertEquals(testee.makeAttributeName(null), "jv_");
+    assertEquals(testee.makeAttributeName(""), "jv_");
+    assertEquals(testee.makeAttributeName("helix"), "jv_helix");
+    assertEquals(testee.makeAttributeName(
+            "Hello World 24"),
             "jv_Hello_World_24");
     assertEquals(
-            ChimeraCommands.makeAttributeName("!this is-a_very*{odd(name"),
+            testee.makeAttributeName(
+                    "!this is-a_very*{odd(name"),
             "jv__this_is_a_very__odd_name");
     // name ending in color gets underscore appended
-    assertEquals(ChimeraCommands.makeAttributeName("helixColor"),
+    assertEquals(testee.makeAttributeName("helixColor"),
             "jv_helixColor_");
   }
 
@@ -295,7 +298,7 @@ public class ChimeraCommandsTest
   public void testGetColourCommand()
   {
     ChimeraCommands testee = new ChimeraCommands();
-    assertEquals(testee.getColourCommand("something", Color.MAGENTA)
+    assertEquals(testee.colourResidues("something", Color.MAGENTA)
             .getCommand(),
             "color #ff00ff something");
   }
index 044a1d9..210c61d 100644 (file)
@@ -299,7 +299,7 @@ public class ChimeraXCommandsTest
   public void testGetColourCommand()
   {
     ChimeraCommands testee = new ChimeraXCommands();
-    assertEquals(testee.getColourCommand("something", Color.MAGENTA)
+    assertEquals(testee.colourResidues("something", Color.MAGENTA)
             .getCommand(),
             "color something #ff00ff");
   }
index 16452a5..c1ad03a 100644 (file)
@@ -52,6 +52,7 @@ import jalview.gui.JvOptionPane;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
 import jalview.io.FileFormats;
+import jalview.io.FileLoader;
 import jalview.schemes.JalviewColourScheme;
 import jalview.structure.AtomSpec;
 import jalview.structure.AtomSpecModel;
@@ -233,7 +234,7 @@ public class AAStructureBindingModelTest
     ssm.setMapping(new SequenceI[] { seq3 }, null, PDB_3,
             DataSourceType.PASTE, null);
 
-    testee = newBindingModel(pdbFiles, seqs, ssm);
+    testee = newBindingModel(pdbFiles, seqs, ssm, null);
   }
 
   /**
@@ -242,10 +243,11 @@ public class AAStructureBindingModelTest
    * @param pdbFiles
    * @param seqs
    * @param ssm
+   * @param alignPanel 
    */
   protected AAStructureBindingModel newBindingModel(PDBEntry[] pdbFiles,
           SequenceI[][] seqs,
-          StructureSelectionManager ssm)
+          StructureSelectionManager ssm, AlignmentViewPanel avp)
   {
     AAStructureBindingModel model = new AAStructureBindingModel(ssm,
             pdbFiles, seqs, null)
@@ -253,7 +255,12 @@ public class AAStructureBindingModelTest
       @Override
       public String[] getStructureFiles()
       {
-        return new String[] { "INLINE1YCS", "INLINE3A6S", "INLINE1OOT" };
+        String[] files = new String[getPdbCount()];
+        for (int i = 0; i < this.getPdbCount(); i++)
+        {
+          files[i] = getPdbEntry(i).getFile();
+        }
+        return files;
       }
 
       @Override
@@ -273,9 +280,11 @@ public class AAStructureBindingModelTest
 
       @Override
       public SequenceRenderer getSequenceRenderer(
-              AlignmentViewPanel alignment)
+              AlignmentViewPanel avp)
       {
-        return null;
+        return avp == null ? null
+                : new jalview.gui.SequenceRenderer(
+                        avp.getAlignViewport());
       }
 
       @Override
@@ -409,10 +418,10 @@ public class AAStructureBindingModelTest
      * load these sequences, coloured by Strand propensity,
      * with columns 2-4 hidden
      */
-    SequenceI seq1 = new Sequence("seq1", "MHRSQSSSGG");
-    SequenceI seq2 = new Sequence("seq2", "MVRSNGGSSS");
-    AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
-    AlignFrame af = new AlignFrame(al, 800, 500);
+    String fasta = ">seq1\nMHRSQSSSGG\n>seq2\nMVRSNGGSSS";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fasta,
+            DataSourceType.PASTE);
+    AlignmentI al = af.getViewport().getAlignment();
     af.changeColour_actionPerformed(JalviewColourScheme.Strand.toString());
     ColumnSelection cs = new ColumnSelection();
     cs.addElement(2);
@@ -420,10 +429,9 @@ public class AAStructureBindingModelTest
     cs.addElement(4);
     af.getViewport().setColumnSelection(cs);
     af.hideSelColumns_actionPerformed(null);
-    SequenceRenderer sr = new jalview.gui.SequenceRenderer(
-            af.getViewport());
+    SequenceI seq1 = al.getSequenceAt(0);
+    SequenceI seq2 = al.getSequenceAt(1);
     SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
-    String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
     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");
@@ -444,14 +452,15 @@ public class AAStructureBindingModelTest
             "B", map, null);
     ssm.addStructureMapping(sm2);
 
-    AAStructureBindingModel binding = newBindingModel(pdbFiles, seqs, ssm);
+    AAStructureBindingModel binding = newBindingModel(pdbFiles, seqs, ssm,
+            af.alignPanel);
 
     /*
      * method under test builds a map of structures residues by colour
      * verify the map holds what it should
      */
-    Map<Object, AtomSpecModel> colours = binding.buildColoursMap(ssm, files,
-            seqs, sr, af.alignPanel);
+    Map<Object, AtomSpecModel> colours = binding.buildColoursMap(ssm, seqs,
+            af.alignPanel);
     ChimeraCommands helper = new ChimeraCommands();
     
     /*