Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package ext.edu.ucsf.rbvi.strucviz2

File StructureManager.java

 

Coverage histogram

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

Code metrics

54
146
32
2
1,041
409
64
0.44
4.56
16
2

Classes

Class Line # Actions
StructureManager 57 146 64
0.00%
StructureManager.ModelType 90 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.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 toggle public StructureManager(boolean haveGUI)
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 toggle public ChimeraManager getChimeraManager()
126    {
127  0 return chimeraManager;
128    }
129   
 
130  0 toggle public boolean openStructures(Collection<List<String>> chimObjNames,
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 toggle public void closeStructures(Set<String> chimObjNames)
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 toggle public File saveChimeraImage()
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 toggle public void closeModel(ChimeraModel model)
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 toggle public void exitChimera()
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 toggle public void clearOnChimeraExit()
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 toggle public void updateCytoscapeSelection()
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 toggle public void cytoscapeSelectionChanged(Map<Long, Boolean> selectedRows)
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 toggle public void updateChimeraSelection()
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 toggle public void chimeraSelectionChanged()
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 toggle public void selectFunctResidues(Collection<ChimeraModel> models)
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 toggle public List<ChimeraStructuralObject> getChimSelectionList()
670    {
671  0 return chimSelectionList;
672    }
673   
 
674  0 toggle public int getChimSelectionCount()
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 toggle public void addChimSelection(ChimeraStructuralObject selectionToAdd)
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 toggle public void removeChimSelection(ChimeraStructuralObject selectionToRemove)
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 toggle public void clearSelectionList()
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 toggle public void updateModels()
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 toggle public void launchModelNavigatorDialog()
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 toggle public boolean isMNDialogOpen()
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 toggle public void modelChanged()
840    {
841    // if (mnDialog != null) {
842    // mnDialog.modelChanged();
843    // }
844    }
845   
846    /**
847    * Inform our interface that the selection has changed
848    */
 
849  0 toggle public void selectionChanged()
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 toggle public void launchAlignDialog(boolean useChains)
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 toggle public List<String> getAllStructureKeys()
895    {
896  0 return Arrays.asList(defaultStructureKeys);
897    }
898   
 
899  0 toggle public List<String> getAllChemStructKeys()
900    {
901  0 return Arrays.asList(defaultChemStructKeys);
902    }
903   
 
904  0 toggle public List<String> getAllResidueKeys()
905    {
906  0 return Arrays.asList(defaultResidueKeys);
907    }
908   
 
909  0 toggle public List<String> getAllChimeraResidueAttributes()
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 toggle public static List<String> getChimeraPaths(boolean isChimeraX)
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 toggle public void setChimeraPathProperty(String path)
997    {
998    // CytoUtils.setDefaultChimeraPath(registrar, chimeraPropertyName,
999    // chimeraPathPropertyKey,
1000    // path);
1001    }
1002   
 
1003  0 toggle public void setStructureSettings(StructureSettings structureSettings)
1004    {
1005  0 this.defaultSettings = structureSettings;
1006    }
1007   
 
1008  0 toggle public String getCurrentChimeraPath(Object object)
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    }