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