6ca663a79d00903c77c66db37a7484b833b6be7f
[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, atom.getChain(),
81               atom.getPdbResNum()));
82     }
83     String labelSpec = sb.toString();
84     if (labelSpec.equals(lastLabelSpec))
85     {
86       return;
87     }
88     StructureCommandI command = new StructureCommand("indicate", labelSpec);
89     executeCommand(command, false);
90
91     lastLabelSpec = labelSpec;
92   }
93
94   @Override
95   public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
96   {
97     return new SequenceRenderer(avp.getAlignViewport());
98   }
99
100   @Override
101   protected List<String> executeCommand(StructureCommandI command,
102           boolean getReply)
103   {
104     // System.out.println(command.toString()); // debug
105     return pymolManager.sendCommand(command, getReply);
106   }
107
108   @Override
109   protected String getModelIdForFile(String file)
110   {
111     return pymolObjects.containsKey(file) ? pymolObjects.get(file) : "";
112   }
113
114   @Override
115   protected ViewerType getViewerType()
116   {
117     return ViewerType.PYMOL;
118   }
119
120   @Override
121   public boolean isViewerRunning()
122   {
123     return pymolManager != null && pymolManager.isPymolLaunched();
124   }
125
126   @Override
127   public void closeViewer(boolean closePymol)
128   {
129     super.closeViewer(closePymol);
130     pymolManager = null;
131   }
132
133   public boolean launchPymol()
134   {
135     if (pymolManager.isPymolLaunched())
136     {
137       return true;
138     }
139
140     Process pymol = pymolManager.launchPymol();
141     if (pymol != null)
142     {
143       // start listening for PyMOL selections - how??
144       startExternalViewerMonitor(pymol);
145     }
146     else
147     {
148       Console.error("Failed to launch PyMOL!");
149     }
150     return pymol != null;
151   }
152
153   public void openFile(PDBEntry pe)
154   {
155     // todo : check not already open, remap / rename, etc
156     String file = pe.getFile();
157     StructureCommandI cmd = getCommandGenerator().loadFile(file);
158
159     /*
160      * a second parameter sets the pdbid as the loaded PyMOL object name
161      */
162     String pdbId = pe.getId();
163     try
164     {
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 }