JAL-3551 fit to window (zoom) command for PyMOL
[jalview.git] / src / jalview / ext / pymol / PymolCommands.java
index 115efa1..f382173 100644 (file)
@@ -3,6 +3,7 @@ package jalview.ext.pymol;
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import jalview.structure.AtomSpecModel;
 import jalview.structure.StructureCommand;
@@ -21,6 +22,8 @@ import jalview.structure.StructureCommandsBase;
  */
 public class PymolCommands extends StructureCommandsBase
 {
+  private static final StructureCommand CLOSE_PYMOL = new StructureCommand("quit");
+
   private static final StructureCommand COLOUR_BY_CHAIN = new StructureCommand("spectrum", "chain");
 
   private static final List<StructureCommandI> COLOR_BY_CHARGE = new ArrayList<>();
@@ -74,8 +77,9 @@ public class PymolCommands extends StructureCommandsBase
   @Override
   public StructureCommandI focusView()
   {
-    // TODO what?
-    return null;
+    // https://pymol.org/dokuwiki/doku.php?id=command:zoom
+    // not currently documented on https://pymolwiki.org/index.php/Category:Commands
+    return new StructureCommand("zoom");
   }
 
   @Override
@@ -190,7 +194,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);
@@ -209,4 +213,118 @@ public class PymolCommands extends StructureCommandsBase
     return new StructureCommand("load", file);
   }
 
+  /**
+   * Overrides the default implementation (which generates concatenated
+   * commands) to generate one per colour (because the XML-RPC interface to
+   * PyMOL only accepts one command at a time)
+   * 
+   * @param colourMap
+   * @return
+   */
+  @Override
+  public List<StructureCommandI> colourBySequence(
+          Map<Object, AtomSpecModel> colourMap)
+  {
+    List<StructureCommandI> commands = new ArrayList<>();
+    for (Object key : colourMap.keySet())
+    {
+      Color colour = (Color) key;
+      final AtomSpecModel colourData = colourMap.get(colour);
+      commands.add(getColourCommand(colourData, colour));
+    }
+  
+    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;
+  }
+
+  @Override
+  public StructureCommandI openSession(String filepath)
+  {
+    // https://pymolwiki.org/index.php/Load
+    // this version of the command has no dependency on file extension
+    return new StructureCommand("load", filepath, "", "0", "pse");
+  }
+
+  @Override
+  public StructureCommandI closeViewer()
+  {
+    // https://pymolwiki.org/index.php/Quit
+    return CLOSE_PYMOL;
+  }
+
 }