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