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