JAL-3949 Complete new abstracted logging framework in jalview.log. Updated log calls...
[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.Cache;
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/Label#examples
74      */
75     StringBuilder sb = new StringBuilder();
76     for (AtomSpec atom : atoms)
77     {
78       // todo promote to StructureCommandsI.showLabel()
79       // todo handle CA|P correctly
80       String modelId = getModelIdForFile(atom.getPdbFile());
81       sb.append(String.format(" %s//%s/%d/CA", modelId,
82               atom.getChain(),
83               atom.getPdbResNum()));
84     }
85     String labelSpec = sb.toString();
86     if (labelSpec.equals(lastLabelSpec))
87     {
88       return;
89     }
90     StructureCommandI command = new StructureCommand("label", labelSpec, LABEL_FORMAT);
91     executeCommand(command, false);
92
93     /*
94      * and remove the label(s) previously shown
95      */
96     if (lastLabelSpec != null)
97     {
98       command = new StructureCommand("label", lastLabelSpec, "");
99       executeCommand(command, false);
100     }
101
102     lastLabelSpec = labelSpec;
103   }
104
105   @Override
106   public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
107   {
108     return new SequenceRenderer(avp.getAlignViewport());
109   }
110
111   @Override
112   protected List<String> executeCommand(StructureCommandI command,
113           boolean getReply)
114   {
115     // System.out.println(command.toString()); // debug
116     return pymolManager.sendCommand(command, getReply);
117   }
118
119   @Override
120   protected String getModelIdForFile(String file)
121   {
122     return pymolObjects.containsKey(file) ? pymolObjects.get(file) : "";
123   }
124
125   @Override
126   protected ViewerType getViewerType()
127   {
128     return ViewerType.PYMOL;
129   }
130
131   @Override
132   public boolean isViewerRunning()
133   {
134     return pymolManager.isPymolLaunched();
135   }
136
137   @Override
138   public void closeViewer(boolean closePymol)
139   {
140     super.closeViewer(closePymol);
141     pymolManager = null;
142   }
143
144   public boolean launchPymol()
145   {
146     if (pymolManager.isPymolLaunched())
147     {
148       return true;
149     }
150
151     Process pymol = pymolManager.launchPymol();
152     if (pymol != null)
153     {
154       // start listening for PyMOL selections - how??
155       startExternalViewerMonitor(pymol);
156     }
157     else
158     {
159       Cache.error("Failed to launch PyMOL!");
160     }
161     return pymol != null;
162   }
163
164   public void openFile(PDBEntry pe)
165   {
166     // todo : check not already open, remap / rename, etc
167     String file = pe.getFile();
168     StructureCommandI cmd = getCommandGenerator().loadFile(file);
169
170     /*
171      * a second parameter sets the pdbid as the loaded PyMOL object name
172      */
173     String pdbId = pe.getId();
174     try {
175       String safePDBId = java.net.URLEncoder.encode(pdbId,"UTF-8");
176       pdbId = safePDBId.replace('%', '_');
177       pdbId = pdbId.replace("-", "__");
178       char fc = pdbId.charAt(0);
179       // put an 's' before any numerics
180       if (fc>='0' && fc<='9')
181       {
182         pdbId = 's'+pdbId;
183       }
184 //      pdbId.replace('-', 0)
185     } catch (Exception x)
186     {
187       Cache.error("Unxpected encoding exception for '"+pdbId+"'",x);
188     }
189     cmd.addParameter(pdbId);
190
191     executeCommand(cmd, false);
192
193     pymolObjects.put(file, pdbId);
194     if (!structureFiles.contains(file))
195     {
196       structureFiles.add(file);
197     }
198     if (getSsm() != null)
199     {
200       getSsm().addStructureViewerListener(this);
201     }
202
203   }
204
205   @Override
206   protected String getModelId(int pdbfnum, String file)
207   {
208     return file;
209   }
210
211   /**
212    * Returns the file extension to use for a saved viewer session file (.pse)
213    * 
214    * @return
215    * @see https://pymolwiki.org/index.php/Save
216    */
217   @Override
218   public String getSessionFileExtension()
219   {
220     return ".pse";
221   }
222
223   @Override
224   public String getHelpURL()
225   {
226     return "https://pymolwiki.org/";
227   }
228
229   /**
230    * Constructs and sends commands to set atom properties for visible Jalview
231    * features on residues mapped to structure
232    * 
233    * @param avp
234    * @return
235    */
236   public int sendFeaturesToViewer(AlignmentViewPanel avp)
237   {
238     // todo pull up this and JalviewChimeraBinding variant
239     Map<String, Map<Object, AtomSpecModel>> featureValues = buildFeaturesMap(
240             avp);
241     List<StructureCommandI> commands = getCommandGenerator()
242             .setAttributes(featureValues);
243     executeCommands(commands, false, null);
244     return commands.size();
245   }
246
247 }