JAL-3906 use indicate to highlight current mousedOver residue in Jalview in a Pymol...
[jalview.git] / src / jalview / gui / PymolBindingModel.java
1 package jalview.gui;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import jalview.api.AlignmentViewPanel;
9 import jalview.bin.Console;
10 import jalview.datamodel.PDBEntry;
11 import jalview.datamodel.SequenceI;
12 import jalview.ext.pymol.PymolCommands;
13 import jalview.ext.pymol.PymolManager;
14 import jalview.gui.StructureViewer.ViewerType;
15 import jalview.structure.AtomSpec;
16 import jalview.structure.AtomSpecModel;
17 import jalview.structure.StructureCommand;
18 import jalview.structure.StructureCommandI;
19 import jalview.structure.StructureSelectionManager;
20 import jalview.structures.models.AAStructureBindingModel;
21
22 public class PymolBindingModel extends AAStructureBindingModel
23 {
24   /*
25    * format for labels shown on structures when mousing over sequence;
26    * see https://pymolwiki.org/index.php/Label#examples
27    * left not final so customisable e.g. with a Groovy script
28    */
29   private static String LABEL_FORMAT = "\"%s %s\" % (resn,resi)";
30
31   private PymolManager pymolManager;
32
33   /*
34    * full paths to structure files opened in PyMOL
35    */
36   List<String> structureFiles = new ArrayList<>();
37
38   /*
39    * lookup from file path to PyMOL object name
40    */
41   Map<String, String> pymolObjects = new HashMap<>();
42
43   private String lastLabelSpec;
44
45   /**
46    * Constructor
47    * 
48    * @param viewer
49    * @param ssm
50    * @param pdbentry
51    * @param sequenceIs
52    */
53   public PymolBindingModel(StructureViewerBase viewer,
54           StructureSelectionManager ssm, PDBEntry[] pdbentry,
55           SequenceI[][] sequenceIs)
56   {
57     super(ssm, pdbentry, sequenceIs, null);
58     pymolManager = new PymolManager();
59     setStructureCommands(new PymolCommands());
60     setViewer(viewer);
61   }
62
63   @Override
64   public String[] getStructureFiles()
65   {
66     return structureFiles.toArray(new String[structureFiles.size()]);
67   }
68
69   @Override
70   public void highlightAtoms(List<AtomSpec> atoms)
71   {
72     /*
73      * https://pymolwiki.org/index.php/indicate#examples
74      */
75     StringBuilder sb = new StringBuilder();
76     for (AtomSpec atom : atoms)
77     {
78       // todo promote to StructureCommandsI.showLabel()
79       String modelId = getModelIdForFile(atom.getPdbFile());
80       sb.append(String.format(" %s//%s/%d/*", modelId,
81               atom.getChain(),
82               atom.getPdbResNum()));
83     }
84     String labelSpec = sb.toString();
85     if (labelSpec.equals(lastLabelSpec))
86     {
87       return;
88     }
89     StructureCommandI command = new StructureCommand("indicate", labelSpec);
90     executeCommand(command, false);
91
92     lastLabelSpec = labelSpec;
93   }
94
95   @Override
96   public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
97   {
98     return new SequenceRenderer(avp.getAlignViewport());
99   }
100
101   @Override
102   protected List<String> executeCommand(StructureCommandI command,
103           boolean getReply)
104   {
105     // System.out.println(command.toString()); // debug
106     return pymolManager.sendCommand(command, getReply);
107   }
108
109   @Override
110   protected String getModelIdForFile(String file)
111   {
112     return pymolObjects.containsKey(file) ? pymolObjects.get(file) : "";
113   }
114
115   @Override
116   protected ViewerType getViewerType()
117   {
118     return ViewerType.PYMOL;
119   }
120
121   @Override
122   public boolean isViewerRunning()
123   {
124     return pymolManager.isPymolLaunched();
125   }
126
127   @Override
128   public void closeViewer(boolean closePymol)
129   {
130     super.closeViewer(closePymol);
131     pymolManager = null;
132   }
133
134   public boolean launchPymol()
135   {
136     if (pymolManager.isPymolLaunched())
137     {
138       return true;
139     }
140
141     Process pymol = pymolManager.launchPymol();
142     if (pymol != null)
143     {
144       // start listening for PyMOL selections - how??
145       startExternalViewerMonitor(pymol);
146     }
147     else
148     {
149       Console.error("Failed to launch PyMOL!");
150     }
151     return pymol != null;
152   }
153
154   public void openFile(PDBEntry pe)
155   {
156     // todo : check not already open, remap / rename, etc
157     String file = pe.getFile();
158     StructureCommandI cmd = getCommandGenerator().loadFile(file);
159
160     /*
161      * a second parameter sets the pdbid as the loaded PyMOL object name
162      */
163     String pdbId = pe.getId();
164     try {
165       String safePDBId = java.net.URLEncoder.encode(pdbId,"UTF-8");
166       pdbId = safePDBId.replace('%', '_');
167       pdbId = pdbId.replace("-", "__");
168       char fc = pdbId.charAt(0);
169       // put an 's' before any numerics
170       if (fc>='0' && fc<='9')
171       {
172         pdbId = 's'+pdbId;
173       }
174 //      pdbId.replace('-', 0)
175     } catch (Exception x)
176     {
177       Console.error("Unxpected encoding exception for '"+pdbId+"'",x);
178     }
179     cmd.addParameter(pdbId);
180
181     executeCommand(cmd, false);
182
183     pymolObjects.put(file, pdbId);
184     if (!structureFiles.contains(file))
185     {
186       structureFiles.add(file);
187     }
188     if (getSsm() != null)
189     {
190       getSsm().addStructureViewerListener(this);
191     }
192
193   }
194
195   @Override
196   protected String getModelId(int pdbfnum, String file)
197   {
198     return file;
199   }
200
201   /**
202    * Returns the file extension to use for a saved viewer session file (.pse)
203    * 
204    * @return
205    * @see https://pymolwiki.org/index.php/Save
206    */
207   @Override
208   public String getSessionFileExtension()
209   {
210     return ".pse";
211   }
212
213   @Override
214   public String getHelpURL()
215   {
216     return "https://pymolwiki.org/";
217   }
218
219   /**
220    * Constructs and sends commands to set atom properties for visible Jalview
221    * features on residues mapped to structure
222    * 
223    * @param avp
224    * @return
225    */
226   public int sendFeaturesToViewer(AlignmentViewPanel avp)
227   {
228     // todo pull up this and JalviewChimeraBinding variant
229     Map<String, Map<Object, AtomSpecModel>> featureValues = buildFeaturesMap(
230             avp);
231     List<StructureCommandI> commands = getCommandGenerator()
232             .setAttributes(featureValues);
233     executeCommands(commands, false, null);
234     return commands.size();
235   }
236
237 }