JAL-3725 restrict mapped virtual feature location to mapped region
[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.log.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     cmd.addParameter(pdbId);
175
176     executeCommand(cmd, false);
177
178     pymolObjects.put(file, pdbId);
179     if (!structureFiles.contains(file))
180     {
181       structureFiles.add(file);
182     }
183     if (getSsm() != null)
184     {
185       getSsm().addStructureViewerListener(this);
186     }
187
188   }
189
190   @Override
191   protected String getModelId(int pdbfnum, String file)
192   {
193     return file;
194   }
195
196   /**
197    * Returns the file extension to use for a saved viewer session file (.pse)
198    * 
199    * @return
200    * @see https://pymolwiki.org/index.php/Save
201    */
202   @Override
203   public String getSessionFileExtension()
204   {
205     return ".pse";
206   }
207
208   @Override
209   public String getHelpURL()
210   {
211     return "https://pymolwiki.org/";
212   }
213
214   /**
215    * Constructs and sends commands to set atom properties for visible Jalview
216    * features on residues mapped to structure
217    * 
218    * @param avp
219    * @return
220    */
221   public int sendFeaturesToViewer(AlignmentViewPanel avp)
222   {
223     // todo pull up this and JalviewChimeraBinding variant
224     Map<String, Map<Object, AtomSpecModel>> featureValues = buildFeaturesMap(
225             avp);
226     List<StructureCommandI> commands = getCommandGenerator()
227             .setAttributes(featureValues);
228     executeCommands(commands, false, null);
229     return commands.size();
230   }
231
232 }