3 * Copyright (c) 2006 The Regents of the University of California.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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,
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.
33 package ext.edu.ucsf.rbvi.strucviz2;
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Locale;
44 import java.util.Properties;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
50 import jalview.bin.Cache;
51 import jalview.gui.Preferences;
54 * This object maintains the relationship between Chimera objects and Cytoscape
58 public class StructureManager
61 * Version numbers to build Windows installation paths for
62 * Chimera https://www.cgl.ucsf.edu/chimera/download.html
63 * ChimeraX http://www.rbvi.ucsf.edu/chimerax/download.html#release
64 * https://www.rbvi.ucsf.edu/trac/ChimeraX/wiki/ChangeLog
65 * These are a fallback for Jalview users who don't save path in Preferences;
66 * these will need to be updated as new versions are released;
67 * deliberately not 'final' (so modifiable using Groovy).
69 * May 2020: 1.14 is Chimera latest, anticipating a few more...
70 * 0.93 is ChimeraX latest, 1.0 expected soon
72 private static String[] CHIMERA_VERSIONS = new String[] { "1.18",
73 "1.17.4", "1.17.3", "1.17.2", "1.17.1", "1.17", "1.16.2", "1.16.1",
74 "1.16", "1.15.2", "1.15.1", "1.15", "1.14.2", "1.14.1", "1.14",
75 "1.13.1", "1.13", "1.12.2", "1.12.1", "1.12", "1.11.2", "1.11.2",
78 // Missing 1.1 as this has known bug see JAL-2422
79 private static String[] CHIMERAX_VERSIONS = new String[] { "1.7", "1.6.1",
80 "1.6", "1.5", "1.4", "1.3", "1.2.5", "1.0", "0.93", "0.92", "0.91",
83 static final String[] defaultStructureKeys = { "Structure", "pdb",
84 "pdbFileName", "PDB ID", "structure", "biopax.xref.PDB", "pdb_ids",
85 "ModelName", "ModelNumber" };
87 static final String[] defaultChemStructKeys = { "Smiles", "smiles",
90 static final String[] defaultResidueKeys = { "FunctionalResidues",
91 "ResidueList", "Residues" };
95 PDB_MODEL, MODBASE_MODEL, SMILES
98 public static Properties pathProps;
100 private String chimeraCommandAttr = "ChimeraCommand";
102 private String chimeraOutputTable = "ChimeraTable";
104 private String chimeraOutputAttr = "ChimeraOutput";
106 private boolean haveGUI = true;
108 private ChimeraManager chimeraManager = null;
110 static private List<ChimeraStructuralObject> chimSelectionList;
112 private boolean ignoreCySelection = false;
114 private File configurationDirectory = null;
116 private static Logger logger = LoggerFactory
117 .getLogger(ext.edu.ucsf.rbvi.strucviz2.StructureManager.class);
119 public StructureManager(boolean haveGUI)
121 this.haveGUI = haveGUI;
122 // Create the Chimera interface
123 chimeraManager = new ChimeraManager(this);
124 chimSelectionList = new ArrayList<>();
125 pathProps = new Properties();
128 public ChimeraManager getChimeraManager()
130 return chimeraManager;
133 public boolean openStructures(Collection<List<String>> chimObjNames,
137 Map<String, List<ChimeraModel>> newModels = new HashMap<>();
138 if (chimObjNames.size() > 0)
140 List<String> names = chimObjNames.iterator().next();
145 for (String chimObjName : names)
147 // get or open the corresponding models if they already exist
148 List<ChimeraModel> currentModels = chimeraManager
149 .getChimeraModels(chimObjName, type);
150 if (currentModels.size() == 0)
152 // open and return models
153 currentModels = chimeraManager.openModel(chimObjName, type);
154 if (currentModels == null)
156 // failed to open model, continue with next
159 // if (type == ModelType.SMILES) {
160 // newModels.put("smiles:" + chimObjName, currentModels);
162 newModels.put(chimObjName, currentModels);
165 for (ChimeraModel currentModel : currentModels)
167 // if not RIN then associate new model with the Cytoscape
169 // if (!currentChimMap.containsKey(currentModel)) {
170 // currentChimMap.put(currentModel, new HashSet<CyIdentifiable>());
181 // if (mnDialog != null) {
182 // mnDialog.modelChanged();
184 // aTask.associate();
189 // TODO: [Release] Handle case where one network is associated with two models
193 * public boolean openStructures(CyNetwork network, Map<CyIdentifiable,
194 * List<String>> chimObjNames, ModelType type) { if
195 * (!chimeraManager.isChimeraLaunched() &&
196 * !chimeraManager.launchChimera(getChimeraPaths(network))) {
197 * logger.error("Chimera could not be launched."); return false; } else if
198 * (chimObjNames.size() == 0) { return false; } else if (network == null) {
199 * return openStructures(chimObjNames.values(), type); }
201 * // potential rins Set<CyNetwork> potentialRINs = new HashSet<CyNetwork>();
202 * // attributes List<String> attrsFound = new ArrayList<String>();
204 * addAll(CytoUtils.getMatchingAttributes(network.getDefaultNodeTable(),
205 * getCurrentStructureKeys(network)));
206 * attrsFound.addAll(CytoUtils.getMatchingAttributes
207 * (network.getDefaultNodeTable(), getCurrentChemStructKeys(network))); // new
208 * models Map<String, List<ChimeraModel>> newModels = new HashMap<String,
209 * List<ChimeraModel>>(); // for each node that has an associated structure
210 * for (CyIdentifiable cyObj : chimObjNames.keySet()) { // get possible res
211 * specs List<String> specsFound = null; if (cyObj instanceof CyNode) {
212 * specsFound = ChimUtils.getResidueKeys(network.getDefaultNodeTable(), cyObj,
213 * attrsFound); } // save node to track its selection and mapping to chimera
214 * objects if (!currentCyMap.containsKey(cyObj)) { currentCyMap.put(cyObj, new
215 * HashSet<ChimeraStructuralObject>()); } // save node to network mapping to
216 * keep track of selection events if (!networkMap.containsKey(cyObj)) {
217 * networkMap.put(cyObj, new HashSet<CyNetwork>()); }
218 * networkMap.get(cyObj).add(network); // for each structure that has to be
219 * opened for (String chimObjName : chimObjNames.get(cyObj)) { // get or open
220 * the corresponding models if they already exist List<ChimeraModel>
221 * currentModels = chimeraManager.getChimeraModels(chimObjName, type); if
222 * (currentModels.size() == 0) { // open and return models currentModels =
223 * chimeraManager.openModel(chimObjName, type); if (currentModels == null) {
224 * // failed to open model, continue with next continue; } // if (type ==
225 * ModelType.SMILES) { // newModels.put("smiles:" + chimObjName,
226 * currentModels); // } else { newModels.put(chimObjName, currentModels); // }
227 * // for each model for (ChimeraModel currentModel : currentModels) { //
228 * check if it is a RIN boolean foundRIN = false; if
229 * (currentModel.getModelType().equals(ModelType.PDB_MODEL)) { // go through
230 * all node annotations and check if any of them is a residue // or a chain if
231 * (cyObj instanceof CyNode && network.containsNode((CyNode) cyObj) &&
232 * specsFound != null && specsFound.size() > 0) { for (String resSpec :
233 * specsFound) { ChimeraStructuralObject res =
234 * ChimUtils.fromAttribute(resSpec, chimeraManager); if (res != null && (res
235 * instanceof ChimeraResidue || res instanceof ChimeraChain)) { // if so,
236 * assume it might be a RIN potentialRINs.add(network); foundRIN = true;
237 * break; } } } else if (cyObj instanceof CyNetwork) { // if cyObj is a
238 * network, check for residue/chain annotations in an // arbitrary node
239 * CyNetwork rinNet = (CyNetwork) cyObj; if (rinNet.getNodeList().size() > 0)
240 * { specsFound = ChimUtils.getResidueKeys( rinNet.getDefaultNodeTable(),
241 * rinNet.getNodeList().get(0), attrsFound); for (String resSpec : specsFound)
242 * { ChimeraStructuralObject res = ChimUtils.fromAttribute( resSpec,
243 * chimeraManager); if (res != null && (res instanceof ChimeraResidue || res
244 * instanceof ChimeraChain)) { potentialRINs.add(network); foundRIN = true;
245 * break; } } } } } if (foundRIN) { continue; } // if not RIN then associate
246 * new model with the Cytoscape // node if
247 * (!currentChimMap.containsKey(currentModel)) {
248 * currentChimMap.put(currentModel, new HashSet<CyIdentifiable>()); } String
249 * cyObjName = network.getRow(cyObj).get(CyNetwork.NAME, String.class); if
250 * (cyObjName != null && cyObjName.endsWith(currentModel.getModelName())) { //
251 * it is a modbase model, associate directly
252 * currentCyMap.get(cyObj).add(currentModel);
253 * currentChimMap.get(currentModel).add(cyObj);
254 * currentModel.addCyObject(cyObj, network); } else if (specsFound != null &&
255 * specsFound.size() > 0) { for (String resSpec : specsFound) {
256 * ChimeraStructuralObject specModel = ChimUtils.fromAttribute( resSpec,
257 * chimeraManager); if (specModel == null &&
258 * resSpec.equals(currentModel.getModelName())) { specModel =
259 * chimeraManager.getChimeraModel( currentModel.getModelNumber(),
260 * currentModel.getSubModelNumber()); } if (specModel != null &&
261 * currentModel.toSpec().equals(specModel.toSpec()) ||
262 * currentModel.getModelName().equals("smiles:" + resSpec)) {
263 * currentCyMap.get(cyObj).add(currentModel);
264 * currentChimMap.get(currentModel).add(cyObj);
265 * currentModel.addCyObject(cyObj, network);
266 * currentModel.setFuncResidues(ChimUtils.parseFuncRes(
267 * getResidueList(network, cyObj), chimObjName)); } } } } } } } // networks
268 * that contain nodes associated to newly opened models // this will usually
269 * be of length 1 for (CyNetwork net : potentialRINs) {
270 * addStructureNetwork(net); } // update dialog if (mnDialog != null) {
271 * mnDialog.modelChanged(); } aTask.associate(); return true; }
273 public void closeStructures(Set<String> chimObjNames)
275 // for each cytoscape object and chimera model pair
276 for (String modelName : chimObjNames)
278 List<ChimeraModel> models = chimeraManager
279 .getChimeraModels(modelName);
280 for (ChimeraModel model : models)
285 // if (mnDialog != null) {
286 // mnDialog.modelChanged();
290 // TODO: [Optional] Can we make a screenshot of a single molecule?
291 public File saveChimeraImage()
296 // Create the temp file name
297 tmpFile = File.createTempFile("structureViz", ".png");
298 chimeraManager.sendChimeraCommand("set bgTransparency", false);
299 chimeraManager.sendChimeraCommand(
300 "copy file " + tmpFile.getAbsolutePath() + " png", true);
301 chimeraManager.sendChimeraCommand("unset bgTransparency", false);
302 } catch (IOException ioe)
305 logger.error("Error writing image", ioe);
310 public void closeModel(ChimeraModel model)
312 // close model in Chimera
313 chimeraManager.closeModel(model);
314 // remove all associations
315 // if (currentChimMap.containsKey(model)) {
316 // for (CyIdentifiable cyObj : model.getCyObjects().keySet()) {
317 // if (cyObj == null) {
319 // } else if (currentCyMap.containsKey(cyObj)) {
320 // currentCyMap.get(cyObj).remove(model);
321 // } else if (cyObj instanceof CyNetwork) {
322 // for (ChimeraResidue residue : model.getResidues()) {
323 // if (currentChimMap.containsKey(residue)) {
324 // for (CyIdentifiable cyObjRes : currentChimMap.get(residue)) {
325 // if (currentCyMap.containsKey(cyObjRes)) {
326 // currentCyMap.get(cyObjRes).remove(residue);
329 // currentChimMap.remove(residue);
334 // currentChimMap.remove(model);
338 // public void addStructureNetwork(CyNetwork rin) {
339 // if (rin == null) {
342 // ChimeraModel model = null;
343 // // the network is not added to the model in the currentChimMap
344 // List<String> attrsFound =
345 // CytoUtils.getMatchingAttributes(rin.getDefaultNodeTable(),
346 // getCurrentStructureKeys(rin));
347 // for (CyNode node : rin.getNodeList()) {
348 // if (!networkMap.containsKey(node)) {
349 // networkMap.put(node, new HashSet<CyNetwork>());
351 // networkMap.get(node).add(rin);
352 // List<String> specsFound =
353 // ChimUtils.getResidueKeys(rin.getDefaultNodeTable(), node,
355 // for (String residueSpec : specsFound) {
356 // // if (!rin.getRow(node).isSet(ChimUtils.RESIDUE_ATTR)) {
359 // // String residueSpec = rin.getRow(node).get(ChimUtils.RESIDUE_ATTR,
361 // ChimeraStructuralObject chimObj = ChimUtils.fromAttribute(residueSpec,
363 // // chimObj.getChimeraModel().addCyObject(node, rin);
364 // if (chimObj == null || chimObj instanceof ChimeraModel) {
367 // model = chimObj.getChimeraModel();
368 // if (!currentCyMap.containsKey(node)) {
369 // currentCyMap.put(node, new HashSet<ChimeraStructuralObject>());
371 // currentCyMap.get(node).add(chimObj);
372 // if (!currentChimMap.containsKey(chimObj)) {
373 // currentChimMap.put(chimObj, new HashSet<CyIdentifiable>());
375 // currentChimMap.get(chimObj).add(node);
378 // if (model != null) {
379 // model.addCyObject(rin, rin);
380 // if (!currentCyMap.containsKey(rin)) {
381 // currentCyMap.put(rin, new HashSet<ChimeraStructuralObject>());
383 // currentCyMap.get(rin).add(model);
387 public void exitChimera()
389 // // exit chimera, invokes clearOnExitChimera
390 // if (mnDialog != null) {
391 // mnDialog.setVisible(false);
394 // if (alDialog != null) {
395 // alDialog.setVisible(false);
397 chimeraManager.exitChimera();
400 // invoked by ChimeraManager whenever Chimera exits
401 public void clearOnChimeraExit()
403 // // clear structures
404 // currentCyMap.clear();
405 // currentChimMap.clear();
406 // networkMap.clear();
407 chimSelectionList.clear();
408 // if (chimTable != null) {
410 // getService(CyTableManager.class)).deleteTable(chimTable.getSUID());
412 // if (mnDialog != null) {
413 // if (mnDialog.isVisible()) {
414 // mnDialog.lostChimera();
415 // mnDialog.setVisible(false);
418 // if (alDialog != null) {
419 // alDialog.setVisible(false);
424 // We need to do this in two passes since some parts of a structure might be
425 // selected and some might not. Our selection model (unfortunately) only
427 // us that something has changed, not what...
428 public void updateCytoscapeSelection()
430 // List<ChimeraStructuralObject> selectedChimObj
431 ignoreCySelection = true;
432 // System.out.println("update Cytoscape selection");
433 // find all possibly selected Cytoscape objects and unselect them
434 // Set<CyNetwork> networks = new HashSet<CyNetwork>();
435 // for (CyIdentifiable currentCyObj : currentCyMap.keySet()) {
436 // if (!networkMap.containsKey(currentCyObj)) {
439 // Set<CyNetwork> currentCyNetworks = networkMap.get(currentCyObj);
440 // if (currentCyNetworks == null || currentCyNetworks.size() == 0) {
444 // for (CyNetwork network : currentCyNetworks) {
445 // if ((currentCyObj instanceof CyNode && network.containsNode((CyNode)
447 // || (currentCyObj instanceof CyEdge && network
448 // .containsEdge((CyEdge) currentCyObj))) {
449 // network.getRow(currentCyObj).set(CyNetwork.SELECTED, false);
450 // networks.add(network);
455 // // select only those associated with selected Chimera objects
456 // Set<CyIdentifiable> currentCyObjs = new HashSet<CyIdentifiable>();
457 // for (ChimeraStructuralObject chimObj : chimSelectionList) {
458 // ChimeraModel currentSelModel = chimObj.getChimeraModel();
459 // if (currentChimMap.containsKey(currentSelModel)) {
460 // currentCyObjs.addAll(currentChimMap.get(currentSelModel));
462 // if (currentChimMap.containsKey(chimObj)) {
463 // currentCyObjs.addAll(currentChimMap.get(chimObj));
465 // // System.out.println(chimObj.toSpec() + ": " +
466 // // currentCyObjs.size());
468 // for (CyIdentifiable cyObj : currentCyObjs) {
469 // // System.out.println(cyObj.toString());
470 // if (cyObj == null || !networkMap.containsKey(cyObj)) {
473 // Set<CyNetwork> currentCyNetworks = networkMap.get(cyObj);
474 // if (currentCyNetworks == null || currentCyNetworks.size() == 0) {
477 // for (CyNetwork network : currentCyNetworks) {
478 // if ((cyObj instanceof CyNode && network.containsNode((CyNode) cyObj))
479 // || (cyObj instanceof CyEdge && network.containsEdge((CyEdge) cyObj))) {
480 // network.getRow(cyObj).set(CyNetwork.SELECTED, true);
481 // networks.add(network);
486 // CyNetworkViewManager cyNetViewManager = (CyNetworkViewManager)
487 // getService(CyNetworkViewManager.class);
488 // // Update network views
489 // for (CyNetwork network : networks) {
490 // Collection<CyNetworkView> views =
491 // cyNetViewManager.getNetworkViews(network);
492 // for (CyNetworkView view : views) {
493 // view.updateView();
496 ignoreCySelection = false;
499 public void cytoscapeSelectionChanged(Map<Long, Boolean> selectedRows)
501 // if (ignoreCySelection || currentCyMap.size() == 0) {
504 // // clearSelectionList();
505 // // System.out.println("cytoscape selection changed");
506 // // iterate over all cy objects with associated models
507 // for (CyIdentifiable cyObj : currentCyMap.keySet()) {
508 // if (cyObj instanceof CyNetwork ||
509 // !selectedRows.containsKey(cyObj.getSUID())) {
512 // for (ChimeraStructuralObject chimObj : currentCyMap.get(cyObj)) {
513 // if (selectedRows.get(cyObj.getSUID())) {
514 // addChimSelection(chimObj);
515 // if (chimObj instanceof ChimeraResidue) {
516 // if (chimObj.getChimeraModel().isSelected()) {
517 // removeChimSelection(chimObj.getChimeraModel());
518 // } else if (chimObj.getChimeraModel()
519 // .getChain(((ChimeraResidue) chimObj).getChainId()).isSelected()) {
520 // removeChimSelection(chimObj.getChimeraModel().getChain(
521 // ((ChimeraResidue) chimObj).getChainId()));
525 // removeChimSelection(chimObj);
526 // if (chimObj.hasSelectedChildren() && chimObj instanceof ChimeraModel) {
527 // for (ChimeraResidue residue : ((ChimeraModel) chimObj)
528 // .getSelectedResidues()) {
529 // removeChimSelection(residue);
535 // System.out.println("selection list: " + getChimSelectionCount());
536 updateChimeraSelection();
540 // Save models in a HashMap/Set for better performance?
541 public void updateChimeraSelection()
543 // System.out.println("update Chimera selection");
545 for (int i = 0; i < chimSelectionList.size(); i++)
547 ChimeraStructuralObject nodeInfo = chimSelectionList.get(i);
548 // we do not care about the model anymore
549 selSpec = selSpec.concat(nodeInfo.toSpec());
550 if (i < chimSelectionList.size() - 1)
555 if (selSpec.length() > 0)
557 chimeraManager.select("sel " + selSpec);
561 chimeraManager.select("~sel");
566 * This is called by the selectionListener to let us know that the user has
567 * changed their selection in Chimera. We need to go back to Chimera to find
568 * out what is currently selected and update our list.
570 public void chimeraSelectionChanged()
572 // System.out.println("Chimera selection changed");
573 clearSelectionList();
574 // Execute the command to get the list of models with selections
575 Map<Integer, ChimeraModel> selectedModelsMap = chimeraManager
576 .getSelectedModels();
577 // Now get the residue-level data
578 chimeraManager.getSelectedResidues(selectedModelsMap);
579 // Get the selected objects
582 for (ChimeraModel selectedModel : selectedModelsMap.values())
584 int modelNumber = selectedModel.getModelNumber();
585 int subModelNumber = selectedModel.getSubModelNumber();
586 // Get the corresponding "real" model
587 if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber))
589 ChimeraModel dataModel = chimeraManager
590 .getChimeraModel(modelNumber, subModelNumber);
591 if (dataModel.getResidueCount() == selectedModel.getResidueCount()
593 .getModelType() == StructureManager.ModelType.SMILES)
595 // Select the entire model
596 addChimSelection(dataModel);
597 // dataModel.setSelected(true);
601 for (ChimeraChain selectedChain : selectedModel.getChains())
603 ChimeraChain dataChain = dataModel
604 .getChain(selectedChain.getChainId());
605 if (selectedChain.getResidueCount() == dataChain
608 addChimSelection(dataChain);
609 // dataChain.setSelected(true);
612 // Need to select individual residues
613 for (ChimeraResidue res : selectedChain.getResidues())
615 String residueIndex = res.getIndex();
616 ChimeraResidue residue = dataChain.getResidue(residueIndex);
621 addChimSelection(residue);
622 // residue.setSelected(true);
625 } // chainIter.hasNext()
628 } // modelIter.hasNext()
629 } catch (Exception ex)
631 logger.warn("Could not update selection", ex);
633 // System.out.println("selection list: " + getChimSelectionCount());
634 // Finally, update the navigator panel
636 updateCytoscapeSelection();
639 public void selectFunctResidues(Collection<ChimeraModel> models)
641 clearSelectionList();
642 for (ChimeraModel model : models)
644 for (ChimeraResidue residue : model.getFuncResidues())
646 addChimSelection(residue);
649 updateChimeraSelection();
650 updateCytoscapeSelection();
654 // public void selectFunctResidues(CyNode node, CyNetwork network) {
655 // clearSelectionList();
656 // if (currentCyMap.containsKey(node)) {
657 // Set<ChimeraStructuralObject> chimObjects = currentCyMap.get(node);
658 // for (ChimeraStructuralObject obj : chimObjects) {
659 // if (obj instanceof ChimeraModel) {
660 // ChimeraModel model = (ChimeraModel) obj;
661 // for (ChimeraResidue residue : model.getFuncResidues()) {
662 // addChimSelection(residue);
667 // updateChimeraSelection();
668 // updateCytoscapeSelection();
669 // selectionChanged();
672 public List<ChimeraStructuralObject> getChimSelectionList()
674 return chimSelectionList;
677 public int getChimSelectionCount()
679 return chimSelectionList.size();
683 * Add a selection to the selection list. This is called primarily by the
684 * Model Navigator Dialog to keep the selections in sync
686 * @param selectionToAdd
687 * the selection to add to our list
689 public void addChimSelection(ChimeraStructuralObject selectionToAdd)
691 if (selectionToAdd != null
692 && !chimSelectionList.contains(selectionToAdd))
694 chimSelectionList.add(selectionToAdd);
695 selectionToAdd.setSelected(true);
700 * Remove a selection from the selection list. This is called primarily by the
701 * Model Navigator Dialog to keep the selections in sync
703 * @param selectionToRemove
704 * the selection to remove from our list
706 public void removeChimSelection(ChimeraStructuralObject selectionToRemove)
708 if (selectionToRemove != null
709 && chimSelectionList.contains(selectionToRemove))
711 chimSelectionList.remove(selectionToRemove);
712 selectionToRemove.setSelected(false);
717 * Clear the list of selected objects
719 public void clearSelectionList()
721 for (ChimeraStructuralObject cso : chimSelectionList)
725 cso.setSelected(false);
728 chimSelectionList.clear();
732 * Associate a new network with the corresponding Chimera objects.
738 * Dump and refresh all of our model/chain/residue info
740 public void updateModels()
742 // Stop all of our listeners while we try to handle this
743 chimeraManager.stopListening();
745 // Get all of the open models
746 List<ChimeraModel> newModelList = chimeraManager.getModelList();
748 // Match them up -- assume that the model #'s haven't changed
749 for (ChimeraModel newModel : newModelList)
751 // Get the color (for our navigator)
752 newModel.setModelColor(chimeraManager.getModelColor(newModel));
754 // Get our model info
755 int modelNumber = newModel.getModelNumber();
756 int subModelNumber = newModel.getSubModelNumber();
758 // If we already know about this model number, get the Structure,
759 // which tells us about the associated CyNode
760 if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber))
762 ChimeraModel oldModel = chimeraManager.getChimeraModel(modelNumber,
764 chimeraManager.removeChimeraModel(modelNumber, subModelNumber);
765 newModel.setModelType(oldModel.getModelType());
766 if (oldModel.getModelType() == ModelType.SMILES)
768 newModel.setModelName(oldModel.getModelName());
770 // re-assign associations to cytoscape objects
771 // Map<CyIdentifiable, CyNetwork> oldModelCyObjs =
772 // oldModel.getCyObjects();
773 // for (CyIdentifiable cyObj : oldModelCyObjs.keySet()) {
774 // // add cy objects to the new model
775 // newModel.addCyObject(cyObj, oldModelCyObjs.get(cyObj));
776 // if (currentCyMap.containsKey(cyObj)) {
777 // currentCyMap.get(cyObj).add(newModel);
778 // if (currentCyMap.get(cyObj).contains(oldModel)) {
779 // currentCyMap.get(cyObj).remove(oldModel);
783 // // add new model to the chimera objects map and remove old model
784 // if (currentChimMap.containsKey(oldModel)) {
785 // currentChimMap.put(newModel, currentChimMap.get(oldModel));
786 // currentChimMap.remove(oldModel);
789 // add new model to ChimeraManager
790 chimeraManager.addChimeraModel(modelNumber, subModelNumber, newModel);
792 // Get the residue information
793 if (newModel.getModelType() != ModelType.SMILES)
795 chimeraManager.addResidues(newModel);
797 // for (CyIdentifiable cyObj : newModel.getCyObjects().keySet()) {
798 // if (cyObj != null && cyObj instanceof CyNetwork) {
799 // addStructureNetwork((CyNetwork) cyObj);
800 // } else if (cyObj != null && cyObj instanceof CyNode) {
801 // newModel.setFuncResidues(ChimUtils.parseFuncRes(
802 // getResidueList(newModel.getCyObjects().get(cyObj), cyObj),
803 // newModel.getModelName()));
808 // associate all models with any node or network
809 // aTask.associate();
811 // Restart all of our listeners
812 chimeraManager.startListening();
816 public void launchModelNavigatorDialog()
818 // TODO: [Optional] Use haveGUI flag
822 // if (mnDialog == null) {
823 // CySwingApplication cyApplication = (CySwingApplication)
824 // getService(CySwingApplication.class);
825 // mnDialog = new ModelNavigatorDialog(cyApplication.getJFrame(), this);
828 // mnDialog.setVisible(true);
831 public boolean isMNDialogOpen()
833 // if (mnDialog != null && mnDialog.isVisible()) {
840 * Invoked by the listener thread.
842 public void modelChanged()
844 // if (mnDialog != null) {
845 // mnDialog.modelChanged();
850 * Inform our interface that the selection has changed
852 public void selectionChanged()
854 // if (mnDialog != null) {
855 // // System.out.println("update dialog selection");
856 // mnDialog.updateSelection(new
857 // ArrayList<ChimeraStructuralObject>(chimSelectionList));
861 public void launchAlignDialog(boolean useChains)
863 // TODO: [Optional] Use haveGUI flag
864 // Sometimes it does not appear in Windows
868 // if (alDialog != null) {
869 // alDialog.setVisible(false);
870 // alDialog.dispose();
872 // System.out.println("launch align dialog");
873 List<ChimeraStructuralObject> chimObjectList = new ArrayList<>();
874 for (ChimeraModel model : chimeraManager.getChimeraModels())
878 for (ChimeraChain chain : model.getChains())
880 chimObjectList.add(chain);
885 chimObjectList.add(model);
888 // Bring up the dialog
889 // CySwingApplication cyApplication = (CySwingApplication)
890 // getService(CySwingApplication.class);
891 // alDialog = new AlignStructuresDialog(cyApplication.getJFrame(), this,
894 // alDialog.setVisible(true);
897 public List<String> getAllStructureKeys()
899 return Arrays.asList(defaultStructureKeys);
902 public List<String> getAllChemStructKeys()
904 return Arrays.asList(defaultChemStructKeys);
907 public List<String> getAllResidueKeys()
909 return Arrays.asList(defaultResidueKeys);
912 public List<String> getAllChimeraResidueAttributes()
914 List<String> attributes = new ArrayList<>();
915 // attributes.addAll(rinManager.getResAttrs());
916 attributes.addAll(chimeraManager.getAttrList());
920 StructureSettings defaultSettings = null;
922 // TODO: [Optional] Change priority of Chimera paths
923 public static List<String> getChimeraPaths(boolean isChimeraX)
925 List<String> pathList = new ArrayList<>();
927 // if no network is available and the settings have been modified by the
931 // For Jalview, Preferences/Cache plays this role instead
932 // if (defaultSettings != null)
934 // String defaultPath = defaultSettings.getChimeraPath();
935 // if (defaultPath != null && !defaultPath.equals(""))
937 // pathList.add(defaultPath);
942 String os = System.getProperty("os.name");
943 String userPath = Cache
944 .getDefault(isChimeraX ? Preferences.CHIMERAX_PATH
945 : Preferences.CHIMERA_PATH, null);
948 * paths are based on getChimeraPaths() in
950 * https://github.com/RBVI/structureViz2/blob/master/src/main/java/edu/ucsf/rbvi/structureViz2/internal/model/StructureManager.java
952 * https://github.com/RBVI/structureVizX/blob/master/src/main/java/edu/ucsf/rbvi/structureVizX/internal/model/StructureManager.java
954 String chimera = isChimeraX ? "ChimeraX" : "Chimera";
955 String chimeraExe = isChimeraX ? "ChimeraX" : "chimera";
958 * Jalview addition: check if path set in user preferences
960 if (userPath != null)
962 // in macos, deal with the user selecting the .app folder
963 boolean adjusted = false;
964 if (os.startsWith("Mac") && userPath.endsWith((".app")))
966 String possiblePath = String.format("%s/Contents/MacOS/%s",
967 userPath, chimeraExe);
968 if (new File(possiblePath).exists())
970 pathList.add(possiblePath);
976 pathList.add(userPath);
980 // Add default installation paths
981 if (os.startsWith("Linux"))
983 // ChimeraX .deb and .rpm packages put symbolic link from
985 pathList.add(String.format("/usr/bin/%s",
986 chimeraExe.toLowerCase(Locale.ROOT)));
987 pathList.add(String.format("/usr/bin/%s", chimeraExe));
989 pathList.add(String.format("/usr/local/bin/%s",
990 chimeraExe.toLowerCase(Locale.ROOT)));
991 pathList.add(String.format("/usr/local/bin/%s", chimeraExe));
993 // these paths also used by .deb and .rpm
994 pathList.add(String.format("/usr/lib/ucsf-%s/bin/%s",
995 chimera.toLowerCase(Locale.ROOT), chimeraExe));
996 pathList.add(String.format("/usr/libexec/UCSF-%s/bin/%s", chimera,
999 pathList.add(String.format("/usr/local/chimera/bin/%s", chimeraExe));
1003 String.format("%s/bin/%s", System.getProperty("user.home"),
1004 chimeraExe.toLowerCase(Locale.ROOT)));
1005 pathList.add(String.format("%s/bin/%s",
1006 System.getProperty("user.home"), chimeraExe));
1007 pathList.add(String.format("%s/opt/bin/%s",
1008 System.getProperty("user.home"),
1009 chimeraExe.toLowerCase(Locale.ROOT)));
1010 pathList.add(String.format("%s/opt/bin/%s",
1011 System.getProperty("user.home"), chimeraExe));
1012 pathList.add(String.format("%s/local/bin/%s",
1013 System.getProperty("user.home"),
1014 chimeraExe.toLowerCase(Locale.ROOT)));
1015 pathList.add(String.format("%s/local/bin/%s",
1016 System.getProperty("user.home"), chimeraExe));
1018 else if (os.startsWith("Windows"))
1020 for (String root : new String[] { "\\Program Files",
1021 "C:\\Program Files", "\\Program Files (x86)",
1022 "C:\\Program Files (x86)", String.format("%s\\AppData\\Local",
1023 System.getProperty("user.home")) })
1025 String[] candidates = isChimeraX ? CHIMERAX_VERSIONS
1027 for (String version : candidates)
1029 // TODO original code doesn't include version in path; which is right?
1030 String path = String.format("%s\\%s %s\\bin\\%s", root, chimera,
1031 version, chimeraExe);
1033 pathList.add(path + ".exe");
1035 // try without a version number too
1036 String path = String.format("%s\\%s\\bin\\%s", root, chimera,
1039 pathList.add(path + ".exe");
1042 else if (os.startsWith("Mac"))
1044 // check for installations with version numbers first
1045 String[] candidates = isChimeraX ? CHIMERAX_VERSIONS
1047 for (String version : candidates)
1050 String.format("/Applications/%s-%s.app/Contents/MacOS/%s",
1051 chimera, version, chimeraExe));
1053 String.format("%s/Applications/%s-%s.app/Contents/MacOS/%s",
1054 System.getProperty("user.home"), chimera, version,
1057 pathList.add(String.format("/Applications/%s.app/Contents/MacOS/%s",
1058 chimera, chimeraExe));
1059 pathList.add(String.format("%s/Applications/%s.app/Contents/MacOS/%s",
1060 System.getProperty("user.home"), chimera, chimeraExe));
1065 public void setChimeraPathProperty(String path)
1067 // CytoUtils.setDefaultChimeraPath(registrar, chimeraPropertyName,
1068 // chimeraPathPropertyKey,
1072 public void setStructureSettings(StructureSettings structureSettings)
1074 this.defaultSettings = structureSettings;
1077 public String getCurrentChimeraPath(Object object)
1079 if (defaultSettings != null)
1081 return defaultSettings.getChimeraPath();
1089 // public void initChimTable() {
1090 // CyTableManager manager = (CyTableManager) getService(CyTableManager.class);
1091 // CyTableFactory factory = (CyTableFactory) getService(CyTableFactory.class);
1092 // for (CyTable table : manager.getGlobalTables()) {
1093 // if (table.getTitle().equals(chimeraOutputTable)) {
1094 // manager.deleteTable(table.getSUID());
1097 // chimTable = factory.createTable(chimeraOutputTable, chimeraCommandAttr,
1100 // manager.addTable(chimTable);
1101 // if (chimTable.getColumn(chimeraOutputAttr) == null) {
1102 // chimTable.createListColumn(chimeraOutputAttr, String.class, false);
1106 // public void addChimReply(String command, List<String> reply) {
1107 // chimTable.getRow(command).set(chimeraOutputAttr, reply);