Class | Line # | Actions | |||
---|---|---|---|---|---|
StructureManager | 57 | 146 | 64 | ||
StructureManager.ModelType | 90 | 0 | 0 |
1 | /* vim: set ts=2: */ | |
2 | /** | |
3 | * Copyright (c) 2006 The Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions, and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above | |
12 | * copyright notice, this list of conditions, and the following | |
13 | * disclaimer in the documentation and/or other materials provided | |
14 | * with the distribution. | |
15 | * 3. Redistributions must acknowledge that this software was | |
16 | * originally developed by the UCSF Computer Graphics Laboratory | |
17 | * under support by the NIH National Center for Research Resources, | |
18 | * grant P41-RR01081. | |
19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY | |
21 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS BE LIABLE | |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT | |
26 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
27 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
28 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
29 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
30 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | * | |
32 | */ | |
33 | package ext.edu.ucsf.rbvi.strucviz2; | |
34 | ||
35 | import java.io.File; | |
36 | import java.io.IOException; | |
37 | import java.util.ArrayList; | |
38 | import java.util.Arrays; | |
39 | import java.util.Collection; | |
40 | import java.util.HashMap; | |
41 | import java.util.List; | |
42 | import java.util.Map; | |
43 | import java.util.Properties; | |
44 | import java.util.Set; | |
45 | ||
46 | import org.slf4j.Logger; | |
47 | import org.slf4j.LoggerFactory; | |
48 | ||
49 | import jalview.bin.Cache; | |
50 | import jalview.gui.Preferences; | |
51 | ||
52 | /** | |
53 | * This object maintains the relationship between Chimera objects and Cytoscape | |
54 | * objects. | |
55 | */ | |
56 | ||
57 | public class StructureManager | |
58 | { | |
59 | /* | |
60 | * Version numbers to build Windows installation paths for | |
61 | * Chimera https://www.cgl.ucsf.edu/chimera/download.html | |
62 | * ChimeraX http://www.rbvi.ucsf.edu/chimerax/download.html#release | |
63 | * https://www.rbvi.ucsf.edu/trac/ChimeraX/wiki/ChangeLog | |
64 | * These are a fallback for Jalview users who don't save path in Preferences; | |
65 | * these will need to be updated as new versions are released; | |
66 | * deliberately not 'final' (so modifiable using Groovy). | |
67 | * | |
68 | * May 2020: 1.14 is Chimera latest, anticipating a few more... | |
69 | * 0.93 is ChimeraX latest, 1.0 expected soon | |
70 | */ | |
71 | private static String[] CHIMERA_VERSIONS = new String[] { "1.16.2", | |
72 | "1.16.1", "1.16", | |
73 | "1.15.2", "1.15.1", "1.15", "1.14.2", "1.14.1", "1.14", | |
74 | "1.13.1", "1.13", "1.12.2", "1.12.1", "1.12", "1.11.2", | |
75 | "1.11.2", "1.11.1", "1.11" }; | |
76 | ||
77 | private static String[] CHIMERAX_VERSIONS = new String[] { "1.0", "0.93", | |
78 | "0.92", "0.91", "0.9" }; | |
79 | ||
80 | static final String[] defaultStructureKeys = { "Structure", "pdb", | |
81 | "pdbFileName", "PDB ID", "structure", "biopax.xref.PDB", "pdb_ids", | |
82 | "ModelName", "ModelNumber" }; | |
83 | ||
84 | static final String[] defaultChemStructKeys = { "Smiles", "smiles", | |
85 | "SMILES" }; | |
86 | ||
87 | static final String[] defaultResidueKeys = { "FunctionalResidues", | |
88 | "ResidueList", "Residues" }; | |
89 | ||
90 | public enum ModelType | |
91 | { | |
92 | PDB_MODEL, MODBASE_MODEL, SMILES | |
93 | }; | |
94 | ||
95 | public static Properties pathProps; | |
96 | ||
97 | private String chimeraCommandAttr = "ChimeraCommand"; | |
98 | ||
99 | private String chimeraOutputTable = "ChimeraTable"; | |
100 | ||
101 | private String chimeraOutputAttr = "ChimeraOutput"; | |
102 | ||
103 | private boolean haveGUI = true; | |
104 | ||
105 | private ChimeraManager chimeraManager = null; | |
106 | ||
107 | static private List<ChimeraStructuralObject> chimSelectionList; | |
108 | ||
109 | private boolean ignoreCySelection = false; | |
110 | ||
111 | private File configurationDirectory = null; | |
112 | ||
113 | private static Logger logger = LoggerFactory | |
114 | .getLogger(ext.edu.ucsf.rbvi.strucviz2.StructureManager.class); | |
115 | ||
116 | 0 | ![]() |
117 | { | |
118 | 0 | this.haveGUI = haveGUI; |
119 | // Create the Chimera interface | |
120 | 0 | chimeraManager = new ChimeraManager(this); |
121 | 0 | chimSelectionList = new ArrayList<>(); |
122 | 0 | pathProps = new Properties(); |
123 | } | |
124 | ||
125 | 0 | ![]() |
126 | { | |
127 | 0 | return chimeraManager; |
128 | } | |
129 | ||
130 | 0 | ![]() |
131 | ModelType type) | |
132 | { | |
133 | // new models | |
134 | 0 | Map<String, List<ChimeraModel>> newModels = new HashMap<>(); |
135 | 0 | if (chimObjNames.size() > 0) |
136 | { | |
137 | 0 | List<String> names = chimObjNames.iterator().next(); |
138 | 0 | if (names == null) |
139 | { | |
140 | 0 | return false; |
141 | } | |
142 | 0 | for (String chimObjName : names) |
143 | { | |
144 | // get or open the corresponding models if they already exist | |
145 | 0 | List<ChimeraModel> currentModels = chimeraManager |
146 | .getChimeraModels(chimObjName, type); | |
147 | 0 | if (currentModels.size() == 0) |
148 | { | |
149 | // open and return models | |
150 | 0 | currentModels = chimeraManager.openModel(chimObjName, type); |
151 | 0 | if (currentModels == null) |
152 | { | |
153 | // failed to open model, continue with next | |
154 | 0 | continue; |
155 | } | |
156 | // if (type == ModelType.SMILES) { | |
157 | // newModels.put("smiles:" + chimObjName, currentModels); | |
158 | // } else { | |
159 | 0 | newModels.put(chimObjName, currentModels); |
160 | // } | |
161 | // for each model | |
162 | 0 | for (ChimeraModel currentModel : currentModels) |
163 | { | |
164 | // if not RIN then associate new model with the Cytoscape | |
165 | // node | |
166 | // if (!currentChimMap.containsKey(currentModel)) { | |
167 | // currentChimMap.put(currentModel, new HashSet<CyIdentifiable>()); | |
168 | // } | |
169 | } | |
170 | } | |
171 | } | |
172 | } | |
173 | else | |
174 | { | |
175 | 0 | return false; |
176 | } | |
177 | // update dialog | |
178 | // if (mnDialog != null) { | |
179 | // mnDialog.modelChanged(); | |
180 | // } | |
181 | // aTask.associate(); | |
182 | 0 | return true; |
183 | ||
184 | } | |
185 | ||
186 | // TODO: [Release] Handle case where one network is associated with two models | |
187 | // that are opened | |
188 | // at the same time | |
189 | /* | |
190 | * public boolean openStructures(CyNetwork network, Map<CyIdentifiable, | |
191 | * List<String>> chimObjNames, ModelType type) { if | |
192 | * (!chimeraManager.isChimeraLaunched() && | |
193 | * !chimeraManager.launchChimera(getChimeraPaths(network))) { | |
194 | * logger.error("Chimera could not be launched."); return false; } else if | |
195 | * (chimObjNames.size() == 0) { return false; } else if (network == null) { | |
196 | * return openStructures(chimObjNames.values(), type); } | |
197 | * | |
198 | * // potential rins Set<CyNetwork> potentialRINs = new HashSet<CyNetwork>(); | |
199 | * // attributes List<String> attrsFound = new ArrayList<String>(); | |
200 | * attrsFound. | |
201 | * addAll(CytoUtils.getMatchingAttributes(network.getDefaultNodeTable(), | |
202 | * getCurrentStructureKeys(network))); | |
203 | * attrsFound.addAll(CytoUtils.getMatchingAttributes | |
204 | * (network.getDefaultNodeTable(), getCurrentChemStructKeys(network))); // new | |
205 | * models Map<String, List<ChimeraModel>> newModels = new HashMap<String, | |
206 | * List<ChimeraModel>>(); // for each node that has an associated structure | |
207 | * for (CyIdentifiable cyObj : chimObjNames.keySet()) { // get possible res | |
208 | * specs List<String> specsFound = null; if (cyObj instanceof CyNode) { | |
209 | * specsFound = ChimUtils.getResidueKeys(network.getDefaultNodeTable(), cyObj, | |
210 | * attrsFound); } // save node to track its selection and mapping to chimera | |
211 | * objects if (!currentCyMap.containsKey(cyObj)) { currentCyMap.put(cyObj, new | |
212 | * HashSet<ChimeraStructuralObject>()); } // save node to network mapping to | |
213 | * keep track of selection events if (!networkMap.containsKey(cyObj)) { | |
214 | * networkMap.put(cyObj, new HashSet<CyNetwork>()); } | |
215 | * networkMap.get(cyObj).add(network); // for each structure that has to be | |
216 | * opened for (String chimObjName : chimObjNames.get(cyObj)) { // get or open | |
217 | * the corresponding models if they already exist List<ChimeraModel> | |
218 | * currentModels = chimeraManager.getChimeraModels(chimObjName, type); if | |
219 | * (currentModels.size() == 0) { // open and return models currentModels = | |
220 | * chimeraManager.openModel(chimObjName, type); if (currentModels == null) { | |
221 | * // failed to open model, continue with next continue; } // if (type == | |
222 | * ModelType.SMILES) { // newModels.put("smiles:" + chimObjName, | |
223 | * currentModels); // } else { newModels.put(chimObjName, currentModels); // } | |
224 | * // for each model for (ChimeraModel currentModel : currentModels) { // | |
225 | * check if it is a RIN boolean foundRIN = false; if | |
226 | * (currentModel.getModelType().equals(ModelType.PDB_MODEL)) { // go through | |
227 | * all node annotations and check if any of them is a residue // or a chain if | |
228 | * (cyObj instanceof CyNode && network.containsNode((CyNode) cyObj) && | |
229 | * specsFound != null && specsFound.size() > 0) { for (String resSpec : | |
230 | * specsFound) { ChimeraStructuralObject res = | |
231 | * ChimUtils.fromAttribute(resSpec, chimeraManager); if (res != null && (res | |
232 | * instanceof ChimeraResidue || res instanceof ChimeraChain)) { // if so, | |
233 | * assume it might be a RIN potentialRINs.add(network); foundRIN = true; | |
234 | * break; } } } else if (cyObj instanceof CyNetwork) { // if cyObj is a | |
235 | * network, check for residue/chain annotations in an // arbitrary node | |
236 | * CyNetwork rinNet = (CyNetwork) cyObj; if (rinNet.getNodeList().size() > 0) | |
237 | * { specsFound = ChimUtils.getResidueKeys( rinNet.getDefaultNodeTable(), | |
238 | * rinNet.getNodeList().get(0), attrsFound); for (String resSpec : specsFound) | |
239 | * { ChimeraStructuralObject res = ChimUtils.fromAttribute( resSpec, | |
240 | * chimeraManager); if (res != null && (res instanceof ChimeraResidue || res | |
241 | * instanceof ChimeraChain)) { potentialRINs.add(network); foundRIN = true; | |
242 | * break; } } } } } if (foundRIN) { continue; } // if not RIN then associate | |
243 | * new model with the Cytoscape // node if | |
244 | * (!currentChimMap.containsKey(currentModel)) { | |
245 | * currentChimMap.put(currentModel, new HashSet<CyIdentifiable>()); } String | |
246 | * cyObjName = network.getRow(cyObj).get(CyNetwork.NAME, String.class); if | |
247 | * (cyObjName != null && cyObjName.endsWith(currentModel.getModelName())) { // | |
248 | * it is a modbase model, associate directly | |
249 | * currentCyMap.get(cyObj).add(currentModel); | |
250 | * currentChimMap.get(currentModel).add(cyObj); | |
251 | * currentModel.addCyObject(cyObj, network); } else if (specsFound != null && | |
252 | * specsFound.size() > 0) { for (String resSpec : specsFound) { | |
253 | * ChimeraStructuralObject specModel = ChimUtils.fromAttribute( resSpec, | |
254 | * chimeraManager); if (specModel == null && | |
255 | * resSpec.equals(currentModel.getModelName())) { specModel = | |
256 | * chimeraManager.getChimeraModel( currentModel.getModelNumber(), | |
257 | * currentModel.getSubModelNumber()); } if (specModel != null && | |
258 | * currentModel.toSpec().equals(specModel.toSpec()) || | |
259 | * currentModel.getModelName().equals("smiles:" + resSpec)) { | |
260 | * currentCyMap.get(cyObj).add(currentModel); | |
261 | * currentChimMap.get(currentModel).add(cyObj); | |
262 | * currentModel.addCyObject(cyObj, network); | |
263 | * currentModel.setFuncResidues(ChimUtils.parseFuncRes( | |
264 | * getResidueList(network, cyObj), chimObjName)); } } } } } } } // networks | |
265 | * that contain nodes associated to newly opened models // this will usually | |
266 | * be of length 1 for (CyNetwork net : potentialRINs) { | |
267 | * addStructureNetwork(net); } // update dialog if (mnDialog != null) { | |
268 | * mnDialog.modelChanged(); } aTask.associate(); return true; } | |
269 | */ | |
270 | 0 | ![]() |
271 | { | |
272 | // for each cytoscape object and chimera model pair | |
273 | 0 | for (String modelName : chimObjNames) |
274 | { | |
275 | 0 | List<ChimeraModel> models = chimeraManager |
276 | .getChimeraModels(modelName); | |
277 | 0 | for (ChimeraModel model : models) |
278 | { | |
279 | 0 | closeModel(model); |
280 | } | |
281 | } | |
282 | // if (mnDialog != null) { | |
283 | // mnDialog.modelChanged(); | |
284 | // } | |
285 | } | |
286 | ||
287 | // TODO: [Optional] Can we make a screenshot of a single molecule? | |
288 | 0 | ![]() |
289 | { | |
290 | 0 | File tmpFile = null; |
291 | 0 | try |
292 | { | |
293 | // Create the temp file name | |
294 | 0 | tmpFile = File.createTempFile("structureViz", ".png"); |
295 | 0 | chimeraManager.sendChimeraCommand("set bgTransparency", false); |
296 | 0 | chimeraManager.sendChimeraCommand( |
297 | "copy file " + tmpFile.getAbsolutePath() + " png", true); | |
298 | 0 | chimeraManager.sendChimeraCommand("unset bgTransparency", false); |
299 | } catch (IOException ioe) | |
300 | { | |
301 | // Log error | |
302 | 0 | logger.error("Error writing image", ioe); |
303 | } | |
304 | 0 | return tmpFile; |
305 | } | |
306 | ||
307 | 0 | ![]() |
308 | { | |
309 | // close model in Chimera | |
310 | 0 | chimeraManager.closeModel(model); |
311 | // remove all associations | |
312 | // if (currentChimMap.containsKey(model)) { | |
313 | // for (CyIdentifiable cyObj : model.getCyObjects().keySet()) { | |
314 | // if (cyObj == null) { | |
315 | // continue; | |
316 | // } else if (currentCyMap.containsKey(cyObj)) { | |
317 | // currentCyMap.get(cyObj).remove(model); | |
318 | // } else if (cyObj instanceof CyNetwork) { | |
319 | // for (ChimeraResidue residue : model.getResidues()) { | |
320 | // if (currentChimMap.containsKey(residue)) { | |
321 | // for (CyIdentifiable cyObjRes : currentChimMap.get(residue)) { | |
322 | // if (currentCyMap.containsKey(cyObjRes)) { | |
323 | // currentCyMap.get(cyObjRes).remove(residue); | |
324 | // } | |
325 | // } | |
326 | // currentChimMap.remove(residue); | |
327 | // } | |
328 | // } | |
329 | // } | |
330 | // } | |
331 | // currentChimMap.remove(model); | |
332 | // } | |
333 | } | |
334 | ||
335 | // public void addStructureNetwork(CyNetwork rin) { | |
336 | // if (rin == null) { | |
337 | // return; | |
338 | // } | |
339 | // ChimeraModel model = null; | |
340 | // // the network is not added to the model in the currentChimMap | |
341 | // List<String> attrsFound = | |
342 | // CytoUtils.getMatchingAttributes(rin.getDefaultNodeTable(), | |
343 | // getCurrentStructureKeys(rin)); | |
344 | // for (CyNode node : rin.getNodeList()) { | |
345 | // if (!networkMap.containsKey(node)) { | |
346 | // networkMap.put(node, new HashSet<CyNetwork>()); | |
347 | // } | |
348 | // networkMap.get(node).add(rin); | |
349 | // List<String> specsFound = | |
350 | // ChimUtils.getResidueKeys(rin.getDefaultNodeTable(), node, | |
351 | // attrsFound); | |
352 | // for (String residueSpec : specsFound) { | |
353 | // // if (!rin.getRow(node).isSet(ChimUtils.RESIDUE_ATTR)) { | |
354 | // // continue; | |
355 | // // } | |
356 | // // String residueSpec = rin.getRow(node).get(ChimUtils.RESIDUE_ATTR, | |
357 | // String.class); | |
358 | // ChimeraStructuralObject chimObj = ChimUtils.fromAttribute(residueSpec, | |
359 | // chimeraManager); | |
360 | // // chimObj.getChimeraModel().addCyObject(node, rin); | |
361 | // if (chimObj == null || chimObj instanceof ChimeraModel) { | |
362 | // continue; | |
363 | // } | |
364 | // model = chimObj.getChimeraModel(); | |
365 | // if (!currentCyMap.containsKey(node)) { | |
366 | // currentCyMap.put(node, new HashSet<ChimeraStructuralObject>()); | |
367 | // } | |
368 | // currentCyMap.get(node).add(chimObj); | |
369 | // if (!currentChimMap.containsKey(chimObj)) { | |
370 | // currentChimMap.put(chimObj, new HashSet<CyIdentifiable>()); | |
371 | // } | |
372 | // currentChimMap.get(chimObj).add(node); | |
373 | // } | |
374 | // } | |
375 | // if (model != null) { | |
376 | // model.addCyObject(rin, rin); | |
377 | // if (!currentCyMap.containsKey(rin)) { | |
378 | // currentCyMap.put(rin, new HashSet<ChimeraStructuralObject>()); | |
379 | // } | |
380 | // currentCyMap.get(rin).add(model); | |
381 | // } | |
382 | // } | |
383 | ||
384 | 0 | ![]() |
385 | { | |
386 | // // exit chimera, invokes clearOnExitChimera | |
387 | // if (mnDialog != null) { | |
388 | // mnDialog.setVisible(false); | |
389 | // mnDialog = null; | |
390 | // } | |
391 | // if (alDialog != null) { | |
392 | // alDialog.setVisible(false); | |
393 | // } | |
394 | 0 | chimeraManager.exitChimera(); |
395 | } | |
396 | ||
397 | // invoked by ChimeraManager whenever Chimera exits | |
398 | 0 | ![]() |
399 | { | |
400 | // // clear structures | |
401 | // currentCyMap.clear(); | |
402 | // currentChimMap.clear(); | |
403 | // networkMap.clear(); | |
404 | 0 | chimSelectionList.clear(); |
405 | // if (chimTable != null) { | |
406 | // ((CyTableManager) | |
407 | // getService(CyTableManager.class)).deleteTable(chimTable.getSUID()); | |
408 | // } | |
409 | // if (mnDialog != null) { | |
410 | // if (mnDialog.isVisible()) { | |
411 | // mnDialog.lostChimera(); | |
412 | // mnDialog.setVisible(false); | |
413 | // } | |
414 | // mnDialog = null; | |
415 | // if (alDialog != null) { | |
416 | // alDialog.setVisible(false); | |
417 | // } | |
418 | // } | |
419 | } | |
420 | ||
421 | // We need to do this in two passes since some parts of a structure might be | |
422 | // selected and some might not. Our selection model (unfortunately) only | |
423 | // tells | |
424 | // us that something has changed, not what... | |
425 | 0 | ![]() |
426 | { | |
427 | // List<ChimeraStructuralObject> selectedChimObj | |
428 | 0 | ignoreCySelection = true; |
429 | // System.out.println("update Cytoscape selection"); | |
430 | // find all possibly selected Cytoscape objects and unselect them | |
431 | // Set<CyNetwork> networks = new HashSet<CyNetwork>(); | |
432 | // for (CyIdentifiable currentCyObj : currentCyMap.keySet()) { | |
433 | // if (!networkMap.containsKey(currentCyObj)) { | |
434 | // continue; | |
435 | // } | |
436 | // Set<CyNetwork> currentCyNetworks = networkMap.get(currentCyObj); | |
437 | // if (currentCyNetworks == null || currentCyNetworks.size() == 0) { | |
438 | // | |
439 | // continue; | |
440 | // } | |
441 | // for (CyNetwork network : currentCyNetworks) { | |
442 | // if ((currentCyObj instanceof CyNode && network.containsNode((CyNode) | |
443 | // currentCyObj)) | |
444 | // || (currentCyObj instanceof CyEdge && network | |
445 | // .containsEdge((CyEdge) currentCyObj))) { | |
446 | // network.getRow(currentCyObj).set(CyNetwork.SELECTED, false); | |
447 | // networks.add(network); | |
448 | // } | |
449 | // } | |
450 | // } | |
451 | // | |
452 | // // select only those associated with selected Chimera objects | |
453 | // Set<CyIdentifiable> currentCyObjs = new HashSet<CyIdentifiable>(); | |
454 | // for (ChimeraStructuralObject chimObj : chimSelectionList) { | |
455 | // ChimeraModel currentSelModel = chimObj.getChimeraModel(); | |
456 | // if (currentChimMap.containsKey(currentSelModel)) { | |
457 | // currentCyObjs.addAll(currentChimMap.get(currentSelModel)); | |
458 | // } | |
459 | // if (currentChimMap.containsKey(chimObj)) { | |
460 | // currentCyObjs.addAll(currentChimMap.get(chimObj)); | |
461 | // } | |
462 | // // System.out.println(chimObj.toSpec() + ": " + | |
463 | // // currentCyObjs.size()); | |
464 | // } | |
465 | // for (CyIdentifiable cyObj : currentCyObjs) { | |
466 | // // System.out.println(cyObj.toString()); | |
467 | // if (cyObj == null || !networkMap.containsKey(cyObj)) { | |
468 | // continue; | |
469 | // } | |
470 | // Set<CyNetwork> currentCyNetworks = networkMap.get(cyObj); | |
471 | // if (currentCyNetworks == null || currentCyNetworks.size() == 0) { | |
472 | // continue; | |
473 | // } | |
474 | // for (CyNetwork network : currentCyNetworks) { | |
475 | // if ((cyObj instanceof CyNode && network.containsNode((CyNode) cyObj)) | |
476 | // || (cyObj instanceof CyEdge && network.containsEdge((CyEdge) cyObj))) { | |
477 | // network.getRow(cyObj).set(CyNetwork.SELECTED, true); | |
478 | // networks.add(network); | |
479 | // } | |
480 | // } | |
481 | // } | |
482 | // | |
483 | // CyNetworkViewManager cyNetViewManager = (CyNetworkViewManager) | |
484 | // getService(CyNetworkViewManager.class); | |
485 | // // Update network views | |
486 | // for (CyNetwork network : networks) { | |
487 | // Collection<CyNetworkView> views = | |
488 | // cyNetViewManager.getNetworkViews(network); | |
489 | // for (CyNetworkView view : views) { | |
490 | // view.updateView(); | |
491 | // } | |
492 | // } | |
493 | 0 | ignoreCySelection = false; |
494 | } | |
495 | ||
496 | 0 | ![]() |
497 | { | |
498 | // if (ignoreCySelection || currentCyMap.size() == 0) { | |
499 | // return; | |
500 | // } | |
501 | // // clearSelectionList(); | |
502 | // // System.out.println("cytoscape selection changed"); | |
503 | // // iterate over all cy objects with associated models | |
504 | // for (CyIdentifiable cyObj : currentCyMap.keySet()) { | |
505 | // if (cyObj instanceof CyNetwork || | |
506 | // !selectedRows.containsKey(cyObj.getSUID())) { | |
507 | // continue; | |
508 | // } | |
509 | // for (ChimeraStructuralObject chimObj : currentCyMap.get(cyObj)) { | |
510 | // if (selectedRows.get(cyObj.getSUID())) { | |
511 | // addChimSelection(chimObj); | |
512 | // if (chimObj instanceof ChimeraResidue) { | |
513 | // if (chimObj.getChimeraModel().isSelected()) { | |
514 | // removeChimSelection(chimObj.getChimeraModel()); | |
515 | // } else if (chimObj.getChimeraModel() | |
516 | // .getChain(((ChimeraResidue) chimObj).getChainId()).isSelected()) { | |
517 | // removeChimSelection(chimObj.getChimeraModel().getChain( | |
518 | // ((ChimeraResidue) chimObj).getChainId())); | |
519 | // } | |
520 | // } | |
521 | // } else { | |
522 | // removeChimSelection(chimObj); | |
523 | // if (chimObj.hasSelectedChildren() && chimObj instanceof ChimeraModel) { | |
524 | // for (ChimeraResidue residue : ((ChimeraModel) chimObj) | |
525 | // .getSelectedResidues()) { | |
526 | // removeChimSelection(residue); | |
527 | // } | |
528 | // } | |
529 | // } | |
530 | // } | |
531 | // } | |
532 | // System.out.println("selection list: " + getChimSelectionCount()); | |
533 | 0 | updateChimeraSelection(); |
534 | 0 | selectionChanged(); |
535 | } | |
536 | ||
537 | // Save models in a HashMap/Set for better performance? | |
538 | 0 | ![]() |
539 | { | |
540 | // System.out.println("update Chimera selection"); | |
541 | 0 | String selSpec = ""; |
542 | 0 | for (int i = 0; i < chimSelectionList.size(); i++) |
543 | { | |
544 | 0 | ChimeraStructuralObject nodeInfo = chimSelectionList.get(i); |
545 | // we do not care about the model anymore | |
546 | 0 | selSpec = selSpec.concat(nodeInfo.toSpec()); |
547 | 0 | if (i < chimSelectionList.size() - 1) |
548 | { | |
549 | 0 | selSpec.concat("|"); |
550 | } | |
551 | } | |
552 | 0 | if (selSpec.length() > 0) |
553 | { | |
554 | 0 | chimeraManager.select("sel " + selSpec); |
555 | } | |
556 | else | |
557 | { | |
558 | 0 | chimeraManager.select("~sel"); |
559 | } | |
560 | } | |
561 | ||
562 | /** | |
563 | * This is called by the selectionListener to let us know that the user has | |
564 | * changed their selection in Chimera. We need to go back to Chimera to find | |
565 | * out what is currently selected and update our list. | |
566 | */ | |
567 | 0 | ![]() |
568 | { | |
569 | // System.out.println("Chimera selection changed"); | |
570 | 0 | clearSelectionList(); |
571 | // Execute the command to get the list of models with selections | |
572 | 0 | Map<Integer, ChimeraModel> selectedModelsMap = chimeraManager |
573 | .getSelectedModels(); | |
574 | // Now get the residue-level data | |
575 | 0 | chimeraManager.getSelectedResidues(selectedModelsMap); |
576 | // Get the selected objects | |
577 | 0 | try |
578 | { | |
579 | 0 | for (ChimeraModel selectedModel : selectedModelsMap.values()) |
580 | { | |
581 | 0 | int modelNumber = selectedModel.getModelNumber(); |
582 | 0 | int subModelNumber = selectedModel.getSubModelNumber(); |
583 | // Get the corresponding "real" model | |
584 | 0 | if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber)) |
585 | { | |
586 | 0 | ChimeraModel dataModel = chimeraManager |
587 | .getChimeraModel(modelNumber, subModelNumber); | |
588 | 0 | if (dataModel.getResidueCount() == selectedModel.getResidueCount() |
589 | || dataModel | |
590 | .getModelType() == StructureManager.ModelType.SMILES) | |
591 | { | |
592 | // Select the entire model | |
593 | 0 | addChimSelection(dataModel); |
594 | // dataModel.setSelected(true); | |
595 | } | |
596 | else | |
597 | { | |
598 | 0 | for (ChimeraChain selectedChain : selectedModel.getChains()) |
599 | { | |
600 | 0 | ChimeraChain dataChain = dataModel |
601 | .getChain(selectedChain.getChainId()); | |
602 | 0 | if (selectedChain.getResidueCount() == dataChain |
603 | .getResidueCount()) | |
604 | { | |
605 | 0 | addChimSelection(dataChain); |
606 | // dataChain.setSelected(true); | |
607 | } | |
608 | // else { | |
609 | // Need to select individual residues | |
610 | 0 | for (ChimeraResidue res : selectedChain.getResidues()) |
611 | { | |
612 | 0 | String residueIndex = res.getIndex(); |
613 | 0 | ChimeraResidue residue = dataChain.getResidue(residueIndex); |
614 | 0 | if (residue == null) |
615 | { | |
616 | 0 | continue; |
617 | } | |
618 | 0 | addChimSelection(residue); |
619 | // residue.setSelected(true); | |
620 | } // resIter.hasNext | |
621 | // } | |
622 | } // chainIter.hasNext() | |
623 | } | |
624 | } | |
625 | } // modelIter.hasNext() | |
626 | } catch (Exception ex) | |
627 | { | |
628 | 0 | logger.warn("Could not update selection", ex); |
629 | } | |
630 | // System.out.println("selection list: " + getChimSelectionCount()); | |
631 | // Finally, update the navigator panel | |
632 | 0 | selectionChanged(); |
633 | 0 | updateCytoscapeSelection(); |
634 | } | |
635 | ||
636 | 0 | ![]() |
637 | { | |
638 | 0 | clearSelectionList(); |
639 | 0 | for (ChimeraModel model : models) |
640 | { | |
641 | 0 | for (ChimeraResidue residue : model.getFuncResidues()) |
642 | { | |
643 | 0 | addChimSelection(residue); |
644 | } | |
645 | } | |
646 | 0 | updateChimeraSelection(); |
647 | 0 | updateCytoscapeSelection(); |
648 | 0 | selectionChanged(); |
649 | } | |
650 | ||
651 | // public void selectFunctResidues(CyNode node, CyNetwork network) { | |
652 | // clearSelectionList(); | |
653 | // if (currentCyMap.containsKey(node)) { | |
654 | // Set<ChimeraStructuralObject> chimObjects = currentCyMap.get(node); | |
655 | // for (ChimeraStructuralObject obj : chimObjects) { | |
656 | // if (obj instanceof ChimeraModel) { | |
657 | // ChimeraModel model = (ChimeraModel) obj; | |
658 | // for (ChimeraResidue residue : model.getFuncResidues()) { | |
659 | // addChimSelection(residue); | |
660 | // } | |
661 | // } | |
662 | // } | |
663 | // } | |
664 | // updateChimeraSelection(); | |
665 | // updateCytoscapeSelection(); | |
666 | // selectionChanged(); | |
667 | // } | |
668 | ||
669 | 0 | ![]() |
670 | { | |
671 | 0 | return chimSelectionList; |
672 | } | |
673 | ||
674 | 0 | ![]() |
675 | { | |
676 | 0 | return chimSelectionList.size(); |
677 | } | |
678 | ||
679 | /** | |
680 | * Add a selection to the selection list. This is called primarily by the | |
681 | * Model Navigator Dialog to keep the selections in sync | |
682 | * | |
683 | * @param selectionToAdd | |
684 | * the selection to add to our list | |
685 | */ | |
686 | 0 | ![]() |
687 | { | |
688 | 0 | if (selectionToAdd != null |
689 | && !chimSelectionList.contains(selectionToAdd)) | |
690 | { | |
691 | 0 | chimSelectionList.add(selectionToAdd); |
692 | 0 | selectionToAdd.setSelected(true); |
693 | } | |
694 | } | |
695 | ||
696 | /** | |
697 | * Remove a selection from the selection list. This is called primarily by the | |
698 | * Model Navigator Dialog to keep the selections in sync | |
699 | * | |
700 | * @param selectionToRemove | |
701 | * the selection to remove from our list | |
702 | */ | |
703 | 0 | ![]() |
704 | { | |
705 | 0 | if (selectionToRemove != null |
706 | && chimSelectionList.contains(selectionToRemove)) | |
707 | { | |
708 | 0 | chimSelectionList.remove(selectionToRemove); |
709 | 0 | selectionToRemove.setSelected(false); |
710 | } | |
711 | } | |
712 | ||
713 | /** | |
714 | * Clear the list of selected objects | |
715 | */ | |
716 | 0 | ![]() |
717 | { | |
718 | 0 | for (ChimeraStructuralObject cso : chimSelectionList) |
719 | { | |
720 | 0 | if (cso != null) |
721 | { | |
722 | 0 | cso.setSelected(false); |
723 | } | |
724 | } | |
725 | 0 | chimSelectionList.clear(); |
726 | } | |
727 | ||
728 | /** | |
729 | * Associate a new network with the corresponding Chimera objects. | |
730 | * | |
731 | * @param network | |
732 | */ | |
733 | ||
734 | /** | |
735 | * Dump and refresh all of our model/chain/residue info | |
736 | */ | |
737 | 0 | ![]() |
738 | { | |
739 | // Stop all of our listeners while we try to handle this | |
740 | 0 | chimeraManager.stopListening(); |
741 | ||
742 | // Get all of the open models | |
743 | 0 | List<ChimeraModel> newModelList = chimeraManager.getModelList(); |
744 | ||
745 | // Match them up -- assume that the model #'s haven't changed | |
746 | 0 | for (ChimeraModel newModel : newModelList) |
747 | { | |
748 | // Get the color (for our navigator) | |
749 | 0 | newModel.setModelColor(chimeraManager.getModelColor(newModel)); |
750 | ||
751 | // Get our model info | |
752 | 0 | int modelNumber = newModel.getModelNumber(); |
753 | 0 | int subModelNumber = newModel.getSubModelNumber(); |
754 | ||
755 | // If we already know about this model number, get the Structure, | |
756 | // which tells us about the associated CyNode | |
757 | 0 | if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber)) |
758 | { | |
759 | 0 | ChimeraModel oldModel = chimeraManager.getChimeraModel(modelNumber, |
760 | subModelNumber); | |
761 | 0 | chimeraManager.removeChimeraModel(modelNumber, subModelNumber); |
762 | 0 | newModel.setModelType(oldModel.getModelType()); |
763 | 0 | if (oldModel.getModelType() == ModelType.SMILES) |
764 | { | |
765 | 0 | newModel.setModelName(oldModel.getModelName()); |
766 | } | |
767 | // re-assign associations to cytoscape objects | |
768 | // Map<CyIdentifiable, CyNetwork> oldModelCyObjs = | |
769 | // oldModel.getCyObjects(); | |
770 | // for (CyIdentifiable cyObj : oldModelCyObjs.keySet()) { | |
771 | // // add cy objects to the new model | |
772 | // newModel.addCyObject(cyObj, oldModelCyObjs.get(cyObj)); | |
773 | // if (currentCyMap.containsKey(cyObj)) { | |
774 | // currentCyMap.get(cyObj).add(newModel); | |
775 | // if (currentCyMap.get(cyObj).contains(oldModel)) { | |
776 | // currentCyMap.get(cyObj).remove(oldModel); | |
777 | // } | |
778 | // } | |
779 | // } | |
780 | // // add new model to the chimera objects map and remove old model | |
781 | // if (currentChimMap.containsKey(oldModel)) { | |
782 | // currentChimMap.put(newModel, currentChimMap.get(oldModel)); | |
783 | // currentChimMap.remove(oldModel); | |
784 | // } | |
785 | } | |
786 | // add new model to ChimeraManager | |
787 | 0 | chimeraManager.addChimeraModel(modelNumber, subModelNumber, newModel); |
788 | ||
789 | // Get the residue information | |
790 | 0 | if (newModel.getModelType() != ModelType.SMILES) |
791 | { | |
792 | 0 | chimeraManager.addResidues(newModel); |
793 | } | |
794 | // for (CyIdentifiable cyObj : newModel.getCyObjects().keySet()) { | |
795 | // if (cyObj != null && cyObj instanceof CyNetwork) { | |
796 | // addStructureNetwork((CyNetwork) cyObj); | |
797 | // } else if (cyObj != null && cyObj instanceof CyNode) { | |
798 | // newModel.setFuncResidues(ChimUtils.parseFuncRes( | |
799 | // getResidueList(newModel.getCyObjects().get(cyObj), cyObj), | |
800 | // newModel.getModelName())); | |
801 | // } | |
802 | // } | |
803 | } | |
804 | ||
805 | // associate all models with any node or network | |
806 | // aTask.associate(); | |
807 | ||
808 | // Restart all of our listeners | |
809 | 0 | chimeraManager.startListening(); |
810 | // Done | |
811 | } | |
812 | ||
813 | 0 | ![]() |
814 | { | |
815 | // TODO: [Optional] Use haveGUI flag | |
816 | // if (!haveGUI) { | |
817 | // return; | |
818 | // } | |
819 | // if (mnDialog == null) { | |
820 | // CySwingApplication cyApplication = (CySwingApplication) | |
821 | // getService(CySwingApplication.class); | |
822 | // mnDialog = new ModelNavigatorDialog(cyApplication.getJFrame(), this); | |
823 | // mnDialog.pack(); | |
824 | // } | |
825 | // mnDialog.setVisible(true); | |
826 | } | |
827 | ||
828 | 0 | ![]() |
829 | { | |
830 | // if (mnDialog != null && mnDialog.isVisible()) { | |
831 | // return true; | |
832 | // } | |
833 | 0 | return false; |
834 | } | |
835 | ||
836 | /** | |
837 | * Invoked by the listener thread. | |
838 | */ | |
839 | 0 | ![]() |
840 | { | |
841 | // if (mnDialog != null) { | |
842 | // mnDialog.modelChanged(); | |
843 | // } | |
844 | } | |
845 | ||
846 | /** | |
847 | * Inform our interface that the selection has changed | |
848 | */ | |
849 | 0 | ![]() |
850 | { | |
851 | // if (mnDialog != null) { | |
852 | // // System.out.println("update dialog selection"); | |
853 | // mnDialog.updateSelection(new | |
854 | // ArrayList<ChimeraStructuralObject>(chimSelectionList)); | |
855 | // } | |
856 | } | |
857 | ||
858 | 0 | ![]() |
859 | { | |
860 | // TODO: [Optional] Use haveGUI flag | |
861 | // Sometimes it does not appear in Windows | |
862 | // if (!haveGUI) { | |
863 | // return; | |
864 | // } | |
865 | // if (alDialog != null) { | |
866 | // alDialog.setVisible(false); | |
867 | // alDialog.dispose(); | |
868 | // } | |
869 | // System.out.println("launch align dialog"); | |
870 | 0 | List<ChimeraStructuralObject> chimObjectList = new ArrayList<>(); |
871 | 0 | for (ChimeraModel model : chimeraManager.getChimeraModels()) |
872 | { | |
873 | 0 | if (useChains) |
874 | { | |
875 | 0 | for (ChimeraChain chain : model.getChains()) |
876 | { | |
877 | 0 | chimObjectList.add(chain); |
878 | } | |
879 | } | |
880 | else | |
881 | { | |
882 | 0 | chimObjectList.add(model); |
883 | } | |
884 | } | |
885 | // Bring up the dialog | |
886 | // CySwingApplication cyApplication = (CySwingApplication) | |
887 | // getService(CySwingApplication.class); | |
888 | // alDialog = new AlignStructuresDialog(cyApplication.getJFrame(), this, | |
889 | // chimObjectList); | |
890 | // alDialog.pack(); | |
891 | // alDialog.setVisible(true); | |
892 | } | |
893 | ||
894 | 0 | ![]() |
895 | { | |
896 | 0 | return Arrays.asList(defaultStructureKeys); |
897 | } | |
898 | ||
899 | 0 | ![]() |
900 | { | |
901 | 0 | return Arrays.asList(defaultChemStructKeys); |
902 | } | |
903 | ||
904 | 0 | ![]() |
905 | { | |
906 | 0 | return Arrays.asList(defaultResidueKeys); |
907 | } | |
908 | ||
909 | 0 | ![]() |
910 | { | |
911 | 0 | List<String> attributes = new ArrayList<>(); |
912 | // attributes.addAll(rinManager.getResAttrs()); | |
913 | 0 | attributes.addAll(chimeraManager.getAttrList()); |
914 | 0 | return attributes; |
915 | } | |
916 | ||
917 | StructureSettings defaultSettings = null; | |
918 | ||
919 | // TODO: [Optional] Change priority of Chimera paths | |
920 | 0 | ![]() |
921 | { | |
922 | 0 | List<String> pathList = new ArrayList<>(); |
923 | ||
924 | // if no network is available and the settings have been modified by the | |
925 | // user, check for a | |
926 | // path to chimera | |
927 | // | |
928 | // For Jalview, Preferences/Cache plays this role instead | |
929 | // if (defaultSettings != null) | |
930 | // { | |
931 | // String defaultPath = defaultSettings.getChimeraPath(); | |
932 | // if (defaultPath != null && !defaultPath.equals("")) | |
933 | // { | |
934 | // pathList.add(defaultPath); | |
935 | // return pathList; | |
936 | // } | |
937 | // } | |
938 | ||
939 | /* | |
940 | * Jalview addition: check if path set in user preferences | |
941 | */ | |
942 | 0 | String userPath = Cache |
943 | 0 | .getDefault(isChimeraX ? Preferences.CHIMERAX_PATH |
944 | : Preferences.CHIMERA_PATH, null); | |
945 | 0 | if (userPath != null) |
946 | { | |
947 | 0 | pathList.add(userPath); |
948 | } | |
949 | ||
950 | /* | |
951 | * paths are based on getChimeraPaths() in | |
952 | * Chimera: | |
953 | * https://github.com/RBVI/structureViz2/blob/master/src/main/java/edu/ucsf/rbvi/structureViz2/internal/model/StructureManager.java | |
954 | * ChimeraX: | |
955 | * https://github.com/RBVI/structureVizX/blob/master/src/main/java/edu/ucsf/rbvi/structureVizX/internal/model/StructureManager.java | |
956 | */ | |
957 | 0 | String chimera = isChimeraX ? "ChimeraX" : "Chimera"; |
958 | 0 | String chimeraExe = isChimeraX ? "ChimeraX" : "chimera"; |
959 | ||
960 | // Add default installation paths | |
961 | 0 | String os = System.getProperty("os.name"); |
962 | 0 | if (os.startsWith("Linux")) |
963 | { | |
964 | // todo should this be /chimeraX/ for ChimeraX? not in structureVizX code | |
965 | 0 | pathList.add("/usr/local/chimera/bin/" + chimeraExe); |
966 | 0 | pathList.add("/usr/local/bin/" + chimeraExe); |
967 | 0 | pathList.add("/usr/bin/" + chimeraExe); |
968 | 0 | pathList.add(System.getProperty("user.home") + "/opt/bin/" + chimeraExe); |
969 | } | |
970 | 0 | else if (os.startsWith("Windows")) |
971 | { | |
972 | 0 | for (String root : new String[] { "\\Program Files", |
973 | "C:\\Program Files", "\\Program Files (x86)", | |
974 | "C:\\Program Files (x86)" }) | |
975 | { | |
976 | 0 | String[] candidates = isChimeraX ? CHIMERAX_VERSIONS |
977 | : CHIMERA_VERSIONS; | |
978 | 0 | for (String version : candidates) |
979 | { | |
980 | // TODO original code doesn't include version in path; which is right? | |
981 | 0 | String path = String.format("%s\\%s %s\\bin\\%s", root, chimera, |
982 | version, chimeraExe); | |
983 | 0 | pathList.add(path); |
984 | 0 | pathList.add(path + ".exe"); |
985 | } | |
986 | } | |
987 | } | |
988 | 0 | else if (os.startsWith("Mac")) |
989 | { | |
990 | 0 | pathList.add(String.format("/Applications/%s.app/Contents/MacOS/%s", |
991 | chimera, chimeraExe)); | |
992 | } | |
993 | 0 | return pathList; |
994 | } | |
995 | ||
996 | 0 | ![]() |
997 | { | |
998 | // CytoUtils.setDefaultChimeraPath(registrar, chimeraPropertyName, | |
999 | // chimeraPathPropertyKey, | |
1000 | // path); | |
1001 | } | |
1002 | ||
1003 | 0 | ![]() |
1004 | { | |
1005 | 0 | this.defaultSettings = structureSettings; |
1006 | } | |
1007 | ||
1008 | 0 | ![]() |
1009 | { | |
1010 | 0 | if (defaultSettings != null) |
1011 | { | |
1012 | 0 | return defaultSettings.getChimeraPath(); |
1013 | } | |
1014 | else | |
1015 | { | |
1016 | 0 | return ""; |
1017 | } | |
1018 | } | |
1019 | ||
1020 | // public void initChimTable() { | |
1021 | // CyTableManager manager = (CyTableManager) getService(CyTableManager.class); | |
1022 | // CyTableFactory factory = (CyTableFactory) getService(CyTableFactory.class); | |
1023 | // for (CyTable table : manager.getGlobalTables()) { | |
1024 | // if (table.getTitle().equals(chimeraOutputTable)) { | |
1025 | // manager.deleteTable(table.getSUID()); | |
1026 | // } | |
1027 | // } | |
1028 | // chimTable = factory.createTable(chimeraOutputTable, chimeraCommandAttr, | |
1029 | // String.class, | |
1030 | // false, true); | |
1031 | // manager.addTable(chimTable); | |
1032 | // if (chimTable.getColumn(chimeraOutputAttr) == null) { | |
1033 | // chimTable.createListColumn(chimeraOutputAttr, String.class, false); | |
1034 | // } | |
1035 | // } | |
1036 | ||
1037 | // public void addChimReply(String command, List<String> reply) { | |
1038 | // chimTable.getRow(command).set(chimeraOutputAttr, reply); | |
1039 | // } | |
1040 | ||
1041 | } |