Clover icon

Coverage Report

  1. Project Clover database Thu Nov 7 2024 10:11:34 GMT
  2. Package ext.edu.ucsf.rbvi.strucviz2

File StructureManager.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart0.png
60% of files have more coverage

Code metrics

62
170
32
2
1,110
466
69
0.41
5.31
16
2.16

Classes

Class Line # Actions
StructureManager 58 170 69
0.00%
StructureManager.ModelType 93 0 0
-1.0 -
 

Contributing tests

No tests hitting this source file were found.

Source view

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