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