JAL-4307 View->Ligands submenu, implementation for Jmol and documentation
[jalview.git] / src / jalview / ext / jmol / JmolCommands.java
index 9c05a37..106d458 100644 (file)
@@ -41,6 +41,7 @@ import jalview.structure.StructureCommandsBase;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.Comparison;
+import jalview.util.Platform;
 
 /**
  * Routines for generating Jmol commands for Jalview/Jmol binding
@@ -53,7 +54,8 @@ public class JmolCommands extends StructureCommandsBase
   private static final StructureCommand SHOW_BACKBONE = new StructureCommand(
           "select *; cartoons off; backbone");
 
-  private static final StructureCommand FOCUS_VIEW = new StructureCommand("zoom 0");
+  private static final StructureCommand FOCUS_VIEW = new StructureCommand(
+          "zoom 0");
 
   private static final StructureCommand COLOUR_ALL_WHITE = new StructureCommand(
           "select *;color white;");
@@ -98,160 +100,6 @@ public class JmolCommands extends StructureCommandsBase
                     c.getBlue());
   }
 
-  @Deprecated
-  public String[] colourBySequence(StructureSelectionManager ssm,
-          String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr,
-          AlignmentViewPanel viewPanel)
-  {
-    // TODO delete method
-
-    FeatureRenderer fr = viewPanel.getFeatureRenderer();
-    FeatureColourFinder finder = new FeatureColourFinder(fr);
-    AlignViewportI viewport = viewPanel.getAlignViewport();
-    HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
-    AlignmentI al = viewport.getAlignment();
-    List<String> cset = new ArrayList<>();
-
-    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
-    {
-      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
-      StringBuilder command = new StringBuilder(128);
-      List<String> str = new ArrayList<>();
-
-      if (mapping == null || mapping.length < 1)
-      {
-        continue;
-      }
-
-      for (int s = 0; s < sequence[pdbfnum].length; s++)
-      {
-        for (int sp, m = 0; m < mapping.length; m++)
-        {
-          if (mapping[m].getSequence() == sequence[pdbfnum][s]
-                  && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
-          {
-            int lastPos = StructureMapping.UNASSIGNED_VALUE;
-            SequenceI asp = al.getSequenceAt(sp);
-            for (int r = 0; r < asp.getLength(); r++)
-            {
-              // no mapping to gaps in sequence
-              if (Comparison.isGap(asp.getCharAt(r)))
-              {
-                continue;
-              }
-              int pos = mapping[m].getPDBResNum(asp.findPosition(r));
-
-              if (pos == lastPos)
-              {
-                continue;
-              }
-              if (pos == StructureMapping.UNASSIGNED_VALUE)
-              {
-                // terminate current colour op
-                if (command.length() > 0
-                        && command.charAt(command.length() - 1) != ';')
-                {
-                  command.append(";");
-                }
-                // reset lastPos
-                lastPos = StructureMapping.UNASSIGNED_VALUE;
-                continue;
-              }
-
-              lastPos = pos;
-
-              Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
-                      finder);
-
-              /*
-               * shade hidden regions darker
-               */
-              if (!cs.isVisible(r))
-              {
-                col = Color.GRAY;
-              }
-
-              String newSelcom = (mapping[m].getChain() != " "
-                      ? ":" + mapping[m].getChain()
-                      : "") + "/" + (pdbfnum + 1) + ".1" + ";color"
-                      + getColourString(col);
-              if (command.length() > newSelcom.length() && command
-                      .substring(command.length() - newSelcom.length())
-                      .equals(newSelcom))
-              {
-                command = JmolCommands.condenseCommand(command, pos);
-                continue;
-              }
-              // TODO: deal with case when buffer is too large for Jmol to parse
-              // - execute command and flush
-
-              if (command.length() > 0
-                      && command.charAt(command.length() - 1) != ';')
-              {
-                command.append(";");
-              }
-
-              if (command.length() > 51200)
-              {
-                // add another chunk
-                str.add(command.toString());
-                command.setLength(0);
-              }
-              command.append("select " + pos);
-              command.append(newSelcom);
-            }
-            // break;
-          }
-        }
-      }
-      {
-        // add final chunk
-        str.add(command.toString());
-        command.setLength(0);
-      }
-      cset.addAll(str);
-
-    }
-    return cset.toArray(new String[cset.size()]);
-  }
-
-  public static StringBuilder condenseCommand(StringBuilder command,
-          int pos)
-  {
-
-    // work back to last 'select'
-    int p = command.length(), q = p;
-    do
-    {
-      p -= 6;
-      if (p < 1)
-      {
-        p = 0;
-      }
-      ;
-    } while ((q = command.indexOf("select", p)) == -1 && p > 0);
-
-    StringBuilder sb = new StringBuilder(command.substring(0, q + 7));
-
-    command = command.delete(0, q + 7);
-
-    String start;
-
-    if (command.indexOf("-") > -1)
-    {
-      start = command.substring(0, command.indexOf("-"));
-    }
-    else
-    {
-      start = command.substring(0, command.indexOf(":"));
-    }
-
-    sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
-
-    return sb;
-  }
-
   @Override
   public StructureCommandI colourByChain()
   {
@@ -265,7 +113,8 @@ public class JmolCommands extends StructureCommandsBase
   }
 
   @Override
-  public List<StructureCommandI> colourByResidues(Map<String, Color> colours)
+  public List<StructureCommandI> colourByResidues(
+          Map<String, Color> colours)
   {
     List<StructureCommandI> cmds = super.colourByResidues(colours);
     cmds.add(0, COLOUR_ALL_WHITE);
@@ -299,7 +148,8 @@ public class JmolCommands extends StructureCommandsBase
           atomSpec.append(" or ");
         }
         first = false;
-        atomSpec.append(":").append(tokens[1]).append(" /").append(tokens[0]);
+        atomSpec.append(":").append(tokens[1]).append(" /")
+                .append(tokens[0]);
       }
     }
 
@@ -331,7 +181,7 @@ public class JmolCommands extends StructureCommandsBase
    */
   @Override
   public List<StructureCommandI> superposeStructures(AtomSpecModel refAtoms,
-          AtomSpecModel atomSpec)
+          AtomSpecModel atomSpec, AtomSpecType backbone)
   {
     StringBuilder sb = new StringBuilder(64);
     String refModel = refAtoms.getModels().iterator().next();
@@ -343,18 +193,35 @@ public class JmolCommands extends StructureCommandsBase
      * command examples don't include modelspec with atoms, getAtomSpec does;
      * it works, so leave it as it is for simplicity
      */
-    sb.append(getAtomSpec(atomSpec, true)).append("}{");
-    sb.append(getAtomSpec(refAtoms, true)).append("}");
+    sb.append(getAtomSpec(atomSpec, backbone)).append("}{");
+    sb.append(getAtomSpec(refAtoms, backbone)).append("}");
     sb.append(" ROTATE TRANSLATE ");
     sb.append(getCommandSeparator());
 
     /*
      * show residues used for superposition as ribbon
      */
-    sb.append("select ").append(getAtomSpec(atomSpec, false)).append("|");
-    sb.append(getAtomSpec(refAtoms, false)).append(getCommandSeparator())
-            .append("cartoons");
+    sb.append("select ")
+            .append(getAtomSpec(atomSpec, AtomSpecType.RESIDUE_ONLY))
+            .append("|");
+    sb.append(getAtomSpec(refAtoms, AtomSpecType.RESIDUE_ONLY))
+            .append(getCommandSeparator()).append("cartoons");
+    return Arrays.asList(new StructureCommand(sb.toString()));
+  }
 
+  @Override
+  public List<StructureCommandI> centerViewOn(List<AtomSpecModel> residues)
+  {
+    StringBuilder sb = new StringBuilder(64);
+    sb.append("center ");
+    for (AtomSpecModel ranges : residues)
+    {
+      if (sb.length() > 9)
+      {
+        sb.append(" or ");
+      }
+      sb.append(getAtomSpec(ranges, AtomSpecType.RESIDUE_ONLY));
+    }
     return Arrays.asList(new StructureCommand(sb.toString()));
   }
 
@@ -378,9 +245,9 @@ 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);
+    StringBuilder sb = new StringBuilder(atomSpec.length() + 20);
     sb.append("select ").append(atomSpec).append(getCommandSeparator())
             .append("color").append(getColourString(colour));
     return new StructureCommand(sb.toString());
@@ -403,7 +270,7 @@ public class JmolCommands extends StructureCommandsBase
    * a separate clause in the {@code compare} (superposition) command.
    */
   @Override
-  public String getAtomSpec(AtomSpecModel model, boolean alphaOnly)
+  public String getAtomSpec(AtomSpecModel model, AtomSpecType specType)
   {
     StringBuilder sb = new StringBuilder(128);
 
@@ -445,6 +312,223 @@ public class JmolCommands extends StructureCommandsBase
   @Override
   public StructureCommandI loadFile(String file)
   {
-    return null;
+    // https://chemapps.stolaf.edu/jmol/docs/#loadfiles
+    return new StructureCommand(
+            "load FILES \"" + Platform.escapeBackslashes(file) + "\"");
+  }
+  @Override
+  public StructureCommandI restoreSession(String filePath)
+  {
+    return new StructureCommand(
+            "restore STATE \"" + Platform.escapeBackslashes(filePath) + "\"");
+  }
+
+  @Override
+  public List<StructureCommandI> showHetatms(List<String> toShow)
+  {
+    // always clear the current hetero cpk display
+    
+    StringBuilder sb = new StringBuilder();
+    sb.append("select hetero; cpk off;");
+    
+    if (toShow != null && !toShow.isEmpty())
+    {
+      // select what was requested
+      sb.append("select ");
+      boolean or = false;
+      for (String k : toShow)
+      {
+        sb.append(or ? " or " : " ");
+        sb.append(k);
+        or = true;
+      }
+      // and show as
+      sb.append("; cpk;");
+    }
+    
+    return Arrays.asList(new StructureCommand(sb.toString()));
+  }
+  /**
+   * Obsolete method, only referenced from
+   * jalview.javascript.MouseOverStructureListener
+   * 
+   * @param ssm
+   * @param files
+   * @param sequence
+   * @param sr
+   * @param viewPanel
+   * @return
+   */
+  @Deprecated
+  public String[] colourBySequence(StructureSelectionManager ssm,
+          String[] files, SequenceI[][] sequence, SequenceRenderer sr,
+          AlignmentViewPanel viewPanel)
+  {
+    // TODO delete method
+
+    FeatureRenderer fr = viewPanel.getFeatureRenderer();
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+    AlignViewportI viewport = viewPanel.getAlignViewport();
+    HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
+    AlignmentI al = viewport.getAlignment();
+    List<String> cset = new ArrayList<>();
+
+    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+    {
+      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
+      StringBuilder command = new StringBuilder(128);
+      List<String> str = new ArrayList<>();
+
+      if (mapping == null || mapping.length < 1)
+      {
+        continue;
+      }
+
+      for (int s = 0; s < sequence[pdbfnum].length; s++)
+      {
+        for (int sp, m = 0; m < mapping.length; m++)
+        {
+          if (mapping[m].getSequence() == sequence[pdbfnum][s]
+                  && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
+          {
+            int lastPos = StructureMapping.UNASSIGNED_VALUE;
+            SequenceI asp = al.getSequenceAt(sp);
+            for (int r = 0; r < asp.getLength(); r++)
+            {
+              // no mapping to gaps in sequence
+              if (Comparison.isGap(asp.getCharAt(r)))
+              {
+                continue;
+              }
+              int pos = mapping[m].getPDBResNum(asp.findPosition(r));
+
+              if (pos == lastPos)
+              {
+                continue;
+              }
+              if (pos == StructureMapping.UNASSIGNED_VALUE)
+              {
+                // terminate current colour op
+                if (command.length() > 0
+                        && command.charAt(command.length() - 1) != ';')
+                {
+                  command.append(";");
+                }
+                // reset lastPos
+                lastPos = StructureMapping.UNASSIGNED_VALUE;
+                continue;
+              }
+
+              lastPos = pos;
+
+              Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
+                      finder);
+
+              /*
+               * shade hidden regions darker
+               */
+              if (!cs.isVisible(r))
+              {
+                col = Color.GRAY;
+              }
+
+              String newSelcom = (mapping[m].getChain() != " "
+                      ? ":" + mapping[m].getChain()
+                      : "") + "/" + (pdbfnum + 1) + ".1" + ";color"
+                      + getColourString(col);
+              if (command.length() > newSelcom.length() && command
+                      .substring(command.length() - newSelcom.length())
+                      .equals(newSelcom))
+              {
+                command = JmolCommands.condenseCommand(command, pos);
+                continue;
+              }
+              // TODO: deal with case when buffer is too large for Jmol to parse
+              // - execute command and flush
+
+              if (command.length() > 0
+                      && command.charAt(command.length() - 1) != ';')
+              {
+                command.append(";");
+              }
+
+              if (command.length() > 51200)
+              {
+                // add another chunk
+                str.add(command.toString());
+                command.setLength(0);
+              }
+              command.append("select " + pos);
+              command.append(newSelcom);
+            }
+            // break;
+          }
+        }
+      }
+      {
+        // add final chunk
+        str.add(command.toString());
+        command.setLength(0);
+      }
+      cset.addAll(str);
+
+    }
+    return cset.toArray(new String[cset.size()]);
+  }
+
+  /**
+   * Helper method
+   * 
+   * @param command
+   * @param pos
+   * @return
+   */
+  @Deprecated
+  private static StringBuilder condenseCommand(StringBuilder command,
+          int pos)
+  {
+
+    // work back to last 'select'
+    int p = command.length(), q = p;
+    do
+    {
+      p -= 6;
+      if (p < 1)
+      {
+        p = 0;
+      }
+      ;
+    } while ((q = command.indexOf("select", p)) == -1 && p > 0);
+
+    StringBuilder sb = new StringBuilder(command.substring(0, q + 7));
+
+    command = command.delete(0, q + 7);
+
+    String start;
+
+    if (command.indexOf("-") > -1)
+    {
+      start = command.substring(0, command.indexOf("-"));
+    }
+    else
+    {
+      start = command.substring(0, command.indexOf(":"));
+    }
+
+    sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
+
+    return sb;
+  }
+
+  @Override
+  public StructureCommandI openSession(String filepath)
+  {
+    return loadFile(filepath);
+  }
+
+  @Override
+  public StructureCommandI closeViewer()
+  {
+    return null; // not an external viewer
   }
 }