JAL-1761 pattern for structure viewer construction from project file
[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 launchPymol()
155   {
156     if (pymolManager.isPymolLaunched())
157     {
158       return true;
159     }
160
161     boolean launched = pymolManager.launchPymol();
162     if (launched)
163     {
164       // start listening for PyMOL selections - how??
165     }
166     else
167     {
168       System.err.println("Failed to launch PyMOL!");
169     }
170     return launched;
171   }
172
173   public void openFile(PDBEntry pe)
174   {
175     // todo : check not already open, remap / rename, etc
176     String file = pe.getFile();
177     StructureCommandI cmd = getCommandGenerator().loadFile(file);
178
179     /*
180      * a second parameter sets the pdbid as the loaded PyMOL object name
181      */
182     String pdbId = pe.getId();
183     cmd.addParameter(pdbId);
184
185     executeCommand(cmd, false);
186
187     pymolObjects.put(file, pdbId);
188     if (!structureFiles.contains(file))
189     {
190       structureFiles.add(file);
191     }
192     if (getSsm() != null)
193     {
194       getSsm().addStructureViewerListener(this);
195     }
196
197   }
198
199   @Override
200   protected String getModelId(int pdbfnum, String file)
201   {
202     return file;
203   }
204
205   /**
206    * Returns the file extension to use for a saved viewer session file (.pse)
207    * 
208    * @return
209    * @see https://pymolwiki.org/index.php/Save
210    */
211   @Override
212   public String getSessionFileExtension()
213   {
214     return ".pse";
215   }
216
217   @Override
218   public String getHelpURL()
219   {
220     return "https://pymolwiki.org/";
221   }
222
223   /**
224    * Constructs and sends commands to set atom properties for visible Jalview
225    * features on residues mapped to structure
226    * 
227    * @param avp
228    * @return
229    */
230   public int sendFeaturesToViewer(AlignmentViewPanel avp)
231   {
232     // todo pull up this and JalviewChimeraBinding variant
233     Map<String, Map<Object, AtomSpecModel>> featureValues = buildFeaturesMap(
234             avp);
235     List<StructureCommandI> commands = getCommandGenerator()
236             .setAttributes(featureValues);
237     executeCommands(commands, false, null);
238     return commands.size();
239   }
240
241 }