JAL-1333 Chimera specific code extracted from the StructureViz library for opening...
[jalview.git] / src / ext / edu / ucsf / rbvi / strucviz2 / StructureManager.java
diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java b/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java
new file mode 100644 (file)
index 0000000..ec956d7
--- /dev/null
@@ -0,0 +1,957 @@
+package ext.edu.ucsf.rbvi.strucviz2;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This object maintains the relationship between Chimera objects and Cytoscape
+ * objects.
+ */
+
+public class StructureManager
+{
+  static final String[] defaultStructureKeys =
+  { "Structure", "pdb", "pdbFileName", "PDB ID", "structure",
+      "biopax.xref.PDB", "pdb_ids", "ModelName", "ModelNumber" };
+
+  static final String[] defaultChemStructKeys =
+  { "Smiles", "smiles", "SMILES" };
+
+  static final String[] defaultResidueKeys =
+  { "FunctionalResidues", "ResidueList", "Residues" };
+
+  private final String chimeraPropertyName = "chimera";
+
+  private final String chimeraPathPropertyKey = "LastChimeraPath";
+
+  public enum ModelType
+  {
+    PDB_MODEL, MODBASE_MODEL, SMILES
+  };
+
+  public static Properties pathProps;
+
+  private String chimeraCommandAttr = "ChimeraCommand";
+
+  private String chimeraOutputTable = "ChimeraTable";
+
+  private String chimeraOutputAttr = "ChimeraOutput";
+
+  private boolean haveGUI = true;
+
+  private ChimeraManager chimeraManager = null;
+
+  static private List<ChimeraStructuralObject> chimSelectionList;
+
+  private boolean ignoreCySelection = false;
+
+  private File configurationDirectory = null;
+
+  private static Logger logger = LoggerFactory
+          .getLogger(ext.edu.ucsf.rbvi.strucviz2.StructureManager.class);
+
+  public StructureManager(boolean haveGUI)
+  {
+    this.haveGUI = haveGUI;
+    // Create the Chimera interface
+    chimeraManager = new ChimeraManager(this);
+    chimSelectionList = new ArrayList<ChimeraStructuralObject>();
+    pathProps = new Properties();
+  }
+
+  public ChimeraManager getChimeraManager()
+  {
+    return chimeraManager;
+  }
+
+  public boolean openStructures(Collection<List<String>> chimObjNames,
+          ModelType type)
+  {
+    // new models
+    Map<String, List<ChimeraModel>> newModels = new HashMap<String, List<ChimeraModel>>();
+    if (chimObjNames.size() > 0)
+    {
+      List<String> names = chimObjNames.iterator().next();
+      if (names == null)
+      {
+        return false;
+      }
+      for (String chimObjName : names)
+      {
+        // get or open the corresponding models if they already exist
+        List<ChimeraModel> currentModels = chimeraManager.getChimeraModels(
+                chimObjName, type);
+        if (currentModels.size() == 0)
+        {
+          // open and return models
+          currentModels = chimeraManager.openModel(chimObjName, type);
+          if (currentModels == null)
+          {
+            // failed to open model, continue with next
+            continue;
+          }
+          // if (type == ModelType.SMILES) {
+          // newModels.put("smiles:" + chimObjName, currentModels);
+          // } else {
+          newModels.put(chimObjName, currentModels);
+          // }
+          // for each model
+          for (ChimeraModel currentModel : currentModels)
+          {
+            // if not RIN then associate new model with the Cytoscape
+            // node
+            // if (!currentChimMap.containsKey(currentModel)) {
+            // currentChimMap.put(currentModel, new HashSet<CyIdentifiable>());
+            // }
+          }
+        }
+      }
+    }
+    else
+    {
+      return false;
+    }
+    // update dialog
+    // if (mnDialog != null) {
+    // mnDialog.modelChanged();
+    // }
+    // aTask.associate();
+    return true;
+
+  }
+
+  // TODO: [Release] Handle case where one network is associated with two models
+  // that are opened
+  // at the same time
+  /*
+   * public boolean openStructures(CyNetwork network, Map<CyIdentifiable,
+   * List<String>> chimObjNames, ModelType type) { if
+   * (!chimeraManager.isChimeraLaunched() &&
+   * !chimeraManager.launchChimera(getChimeraPaths(network))) {
+   * logger.error("Chimera could not be launched."); return false; } else if
+   * (chimObjNames.size() == 0) { return false; } else if (network == null) {
+   * return openStructures(chimObjNames.values(), type); }
+   * 
+   * // potential rins Set<CyNetwork> potentialRINs = new HashSet<CyNetwork>();
+   * // attributes List<String> attrsFound = new ArrayList<String>();
+   * attrsFound.
+   * addAll(CytoUtils.getMatchingAttributes(network.getDefaultNodeTable(),
+   * getCurrentStructureKeys(network)));
+   * attrsFound.addAll(CytoUtils.getMatchingAttributes
+   * (network.getDefaultNodeTable(), getCurrentChemStructKeys(network))); // new
+   * models Map<String, List<ChimeraModel>> newModels = new HashMap<String,
+   * List<ChimeraModel>>(); // for each node that has an associated structure
+   * for (CyIdentifiable cyObj : chimObjNames.keySet()) { // get possible res
+   * specs List<String> specsFound = null; if (cyObj instanceof CyNode) {
+   * specsFound = ChimUtils.getResidueKeys(network.getDefaultNodeTable(), cyObj,
+   * attrsFound); } // save node to track its selection and mapping to chimera
+   * objects if (!currentCyMap.containsKey(cyObj)) { currentCyMap.put(cyObj, new
+   * HashSet<ChimeraStructuralObject>()); } // save node to network mapping to
+   * keep track of selection events if (!networkMap.containsKey(cyObj)) {
+   * networkMap.put(cyObj, new HashSet<CyNetwork>()); }
+   * networkMap.get(cyObj).add(network); // for each structure that has to be
+   * opened for (String chimObjName : chimObjNames.get(cyObj)) { // get or open
+   * the corresponding models if they already exist List<ChimeraModel>
+   * currentModels = chimeraManager.getChimeraModels(chimObjName, type); if
+   * (currentModels.size() == 0) { // open and return models currentModels =
+   * chimeraManager.openModel(chimObjName, type); if (currentModels == null) {
+   * // failed to open model, continue with next continue; } // if (type ==
+   * ModelType.SMILES) { // newModels.put("smiles:" + chimObjName,
+   * currentModels); // } else { newModels.put(chimObjName, currentModels); // }
+   * // for each model for (ChimeraModel currentModel : currentModels) { //
+   * check if it is a RIN boolean foundRIN = false; if
+   * (currentModel.getModelType().equals(ModelType.PDB_MODEL)) { // go through
+   * all node annotations and check if any of them is a residue // or a chain if
+   * (cyObj instanceof CyNode && network.containsNode((CyNode) cyObj) &&
+   * specsFound != null && specsFound.size() > 0) { for (String resSpec :
+   * specsFound) { ChimeraStructuralObject res =
+   * ChimUtils.fromAttribute(resSpec, chimeraManager); if (res != null && (res
+   * instanceof ChimeraResidue || res instanceof ChimeraChain)) { // if so,
+   * assume it might be a RIN potentialRINs.add(network); foundRIN = true;
+   * break; } } } else if (cyObj instanceof CyNetwork) { // if cyObj is a
+   * network, check for residue/chain annotations in an // arbitrary node
+   * CyNetwork rinNet = (CyNetwork) cyObj; if (rinNet.getNodeList().size() > 0)
+   * { specsFound = ChimUtils.getResidueKeys( rinNet.getDefaultNodeTable(),
+   * rinNet.getNodeList().get(0), attrsFound); for (String resSpec : specsFound)
+   * { ChimeraStructuralObject res = ChimUtils.fromAttribute( resSpec,
+   * chimeraManager); if (res != null && (res instanceof ChimeraResidue || res
+   * instanceof ChimeraChain)) { potentialRINs.add(network); foundRIN = true;
+   * break; } } } } } if (foundRIN) { continue; } // if not RIN then associate
+   * new model with the Cytoscape // node if
+   * (!currentChimMap.containsKey(currentModel)) {
+   * currentChimMap.put(currentModel, new HashSet<CyIdentifiable>()); } String
+   * cyObjName = network.getRow(cyObj).get(CyNetwork.NAME, String.class); if
+   * (cyObjName != null && cyObjName.endsWith(currentModel.getModelName())) { //
+   * it is a modbase model, associate directly
+   * currentCyMap.get(cyObj).add(currentModel);
+   * currentChimMap.get(currentModel).add(cyObj);
+   * currentModel.addCyObject(cyObj, network); } else if (specsFound != null &&
+   * specsFound.size() > 0) { for (String resSpec : specsFound) {
+   * ChimeraStructuralObject specModel = ChimUtils.fromAttribute( resSpec,
+   * chimeraManager); if (specModel == null &&
+   * resSpec.equals(currentModel.getModelName())) { specModel =
+   * chimeraManager.getChimeraModel( currentModel.getModelNumber(),
+   * currentModel.getSubModelNumber()); } if (specModel != null &&
+   * currentModel.toSpec().equals(specModel.toSpec()) ||
+   * currentModel.getModelName().equals("smiles:" + resSpec)) {
+   * currentCyMap.get(cyObj).add(currentModel);
+   * currentChimMap.get(currentModel).add(cyObj);
+   * currentModel.addCyObject(cyObj, network);
+   * currentModel.setFuncResidues(ChimUtils.parseFuncRes(
+   * getResidueList(network, cyObj), chimObjName)); } } } } } } } // networks
+   * that contain nodes associated to newly opened models // this will usually
+   * be of length 1 for (CyNetwork net : potentialRINs) {
+   * addStructureNetwork(net); } // update dialog if (mnDialog != null) {
+   * mnDialog.modelChanged(); } aTask.associate(); return true; }
+   */
+  public void closeStructures(Set<String> chimObjNames)
+  {
+    // for each cytoscape object and chimera model pair
+    for (String modelName : chimObjNames)
+    {
+      List<ChimeraModel> models = chimeraManager
+              .getChimeraModels(modelName);
+      for (ChimeraModel model : models)
+      {
+        closeModel(model);
+      }
+    }
+    // if (mnDialog != null) {
+    // mnDialog.modelChanged();
+    // }
+  }
+
+  // TODO: [Optional] Can we make a screenshot of a single molecule?
+  public File saveChimeraImage()
+  {
+    File tmpFile = null;
+    try
+    {
+      // Create the temp file name
+      tmpFile = File.createTempFile("structureViz", ".png");
+      chimeraManager.sendChimeraCommand("set bgTransparency", false);
+      chimeraManager.sendChimeraCommand(
+              "copy file " + tmpFile.getAbsolutePath() + " png", true);
+      chimeraManager.sendChimeraCommand("unset bgTransparency", false);
+    } catch (IOException ioe)
+    {
+      // Log error
+      logger.error("Error writing image", ioe);
+    }
+    return tmpFile;
+  }
+
+  public void closeModel(ChimeraModel model)
+  {
+    // close model in Chimera
+    chimeraManager.closeModel(model);
+    // remove all associations
+    // if (currentChimMap.containsKey(model)) {
+    // for (CyIdentifiable cyObj : model.getCyObjects().keySet()) {
+    // if (cyObj == null) {
+    // continue;
+    // } else if (currentCyMap.containsKey(cyObj)) {
+    // currentCyMap.get(cyObj).remove(model);
+    // } else if (cyObj instanceof CyNetwork) {
+    // for (ChimeraResidue residue : model.getResidues()) {
+    // if (currentChimMap.containsKey(residue)) {
+    // for (CyIdentifiable cyObjRes : currentChimMap.get(residue)) {
+    // if (currentCyMap.containsKey(cyObjRes)) {
+    // currentCyMap.get(cyObjRes).remove(residue);
+    // }
+    // }
+    // currentChimMap.remove(residue);
+    // }
+    // }
+    // }
+    // }
+    // currentChimMap.remove(model);
+    // }
+  }
+
+  // public void addStructureNetwork(CyNetwork rin) {
+  // if (rin == null) {
+  // return;
+  // }
+  // ChimeraModel model = null;
+  // // the network is not added to the model in the currentChimMap
+  // List<String> attrsFound =
+  // CytoUtils.getMatchingAttributes(rin.getDefaultNodeTable(),
+  // getCurrentStructureKeys(rin));
+  // for (CyNode node : rin.getNodeList()) {
+  // if (!networkMap.containsKey(node)) {
+  // networkMap.put(node, new HashSet<CyNetwork>());
+  // }
+  // networkMap.get(node).add(rin);
+  // List<String> specsFound =
+  // ChimUtils.getResidueKeys(rin.getDefaultNodeTable(), node,
+  // attrsFound);
+  // for (String residueSpec : specsFound) {
+  // // if (!rin.getRow(node).isSet(ChimUtils.RESIDUE_ATTR)) {
+  // // continue;
+  // // }
+  // // String residueSpec = rin.getRow(node).get(ChimUtils.RESIDUE_ATTR,
+  // String.class);
+  // ChimeraStructuralObject chimObj = ChimUtils.fromAttribute(residueSpec,
+  // chimeraManager);
+  // // chimObj.getChimeraModel().addCyObject(node, rin);
+  // if (chimObj == null || chimObj instanceof ChimeraModel) {
+  // continue;
+  // }
+  // model = chimObj.getChimeraModel();
+  // if (!currentCyMap.containsKey(node)) {
+  // currentCyMap.put(node, new HashSet<ChimeraStructuralObject>());
+  // }
+  // currentCyMap.get(node).add(chimObj);
+  // if (!currentChimMap.containsKey(chimObj)) {
+  // currentChimMap.put(chimObj, new HashSet<CyIdentifiable>());
+  // }
+  // currentChimMap.get(chimObj).add(node);
+  // }
+  // }
+  // if (model != null) {
+  // model.addCyObject(rin, rin);
+  // if (!currentCyMap.containsKey(rin)) {
+  // currentCyMap.put(rin, new HashSet<ChimeraStructuralObject>());
+  // }
+  // currentCyMap.get(rin).add(model);
+  // }
+  // }
+
+  public void exitChimera()
+  {
+    // // exit chimera, invokes clearOnExitChimera
+    // if (mnDialog != null) {
+    // mnDialog.setVisible(false);
+    // mnDialog = null;
+    // }
+    // if (alDialog != null) {
+    // alDialog.setVisible(false);
+    // }
+    chimeraManager.exitChimera();
+  }
+
+  // invoked by ChimeraManager whenever Chimera exits
+  public void clearOnChimeraExit()
+  {
+    // // clear structures
+    // currentCyMap.clear();
+    // currentChimMap.clear();
+    // networkMap.clear();
+    chimSelectionList.clear();
+    // if (chimTable != null) {
+    // ((CyTableManager)
+    // getService(CyTableManager.class)).deleteTable(chimTable.getSUID());
+    // }
+    // if (mnDialog != null) {
+    // if (mnDialog.isVisible()) {
+    // mnDialog.lostChimera();
+    // mnDialog.setVisible(false);
+    // }
+    // mnDialog = null;
+    // if (alDialog != null) {
+    // alDialog.setVisible(false);
+    // }
+    // }
+  }
+
+  // We need to do this in two passes since some parts of a structure might be
+  // selected and some might not. Our selection model (unfortunately) only
+  // tells
+  // us that something has changed, not what...
+  public void updateCytoscapeSelection()
+  {
+    // List<ChimeraStructuralObject> selectedChimObj
+    ignoreCySelection = true;
+    // System.out.println("update Cytoscape selection");
+    // find all possibly selected Cytoscape objects and unselect them
+    // Set<CyNetwork> networks = new HashSet<CyNetwork>();
+    // for (CyIdentifiable currentCyObj : currentCyMap.keySet()) {
+    // if (!networkMap.containsKey(currentCyObj)) {
+    // continue;
+    // }
+    // Set<CyNetwork> currentCyNetworks = networkMap.get(currentCyObj);
+    // if (currentCyNetworks == null || currentCyNetworks.size() == 0) {
+    //
+    // continue;
+    // }
+    // for (CyNetwork network : currentCyNetworks) {
+    // if ((currentCyObj instanceof CyNode && network.containsNode((CyNode)
+    // currentCyObj))
+    // || (currentCyObj instanceof CyEdge && network
+    // .containsEdge((CyEdge) currentCyObj))) {
+    // network.getRow(currentCyObj).set(CyNetwork.SELECTED, false);
+    // networks.add(network);
+    // }
+    // }
+    // }
+    //
+    // // select only those associated with selected Chimera objects
+    // Set<CyIdentifiable> currentCyObjs = new HashSet<CyIdentifiable>();
+    // for (ChimeraStructuralObject chimObj : chimSelectionList) {
+    // ChimeraModel currentSelModel = chimObj.getChimeraModel();
+    // if (currentChimMap.containsKey(currentSelModel)) {
+    // currentCyObjs.addAll(currentChimMap.get(currentSelModel));
+    // }
+    // if (currentChimMap.containsKey(chimObj)) {
+    // currentCyObjs.addAll(currentChimMap.get(chimObj));
+    // }
+    // // System.out.println(chimObj.toSpec() + ": " +
+    // // currentCyObjs.size());
+    // }
+    // for (CyIdentifiable cyObj : currentCyObjs) {
+    // // System.out.println(cyObj.toString());
+    // if (cyObj == null || !networkMap.containsKey(cyObj)) {
+    // continue;
+    // }
+    // Set<CyNetwork> currentCyNetworks = networkMap.get(cyObj);
+    // if (currentCyNetworks == null || currentCyNetworks.size() == 0) {
+    // continue;
+    // }
+    // for (CyNetwork network : currentCyNetworks) {
+    // if ((cyObj instanceof CyNode && network.containsNode((CyNode) cyObj))
+    // || (cyObj instanceof CyEdge && network.containsEdge((CyEdge) cyObj))) {
+    // network.getRow(cyObj).set(CyNetwork.SELECTED, true);
+    // networks.add(network);
+    // }
+    // }
+    // }
+    //
+    // CyNetworkViewManager cyNetViewManager = (CyNetworkViewManager)
+    // getService(CyNetworkViewManager.class);
+    // // Update network views
+    // for (CyNetwork network : networks) {
+    // Collection<CyNetworkView> views =
+    // cyNetViewManager.getNetworkViews(network);
+    // for (CyNetworkView view : views) {
+    // view.updateView();
+    // }
+    // }
+    ignoreCySelection = false;
+  }
+
+  public void cytoscapeSelectionChanged(Map<Long, Boolean> selectedRows)
+  {
+    // if (ignoreCySelection || currentCyMap.size() == 0) {
+    // return;
+    // }
+    // // clearSelectionList();
+    // // System.out.println("cytoscape selection changed");
+    // // iterate over all cy objects with associated models
+    // for (CyIdentifiable cyObj : currentCyMap.keySet()) {
+    // if (cyObj instanceof CyNetwork ||
+    // !selectedRows.containsKey(cyObj.getSUID())) {
+    // continue;
+    // }
+    // for (ChimeraStructuralObject chimObj : currentCyMap.get(cyObj)) {
+    // if (selectedRows.get(cyObj.getSUID())) {
+    // addChimSelection(chimObj);
+    // if (chimObj instanceof ChimeraResidue) {
+    // if (chimObj.getChimeraModel().isSelected()) {
+    // removeChimSelection(chimObj.getChimeraModel());
+    // } else if (chimObj.getChimeraModel()
+    // .getChain(((ChimeraResidue) chimObj).getChainId()).isSelected()) {
+    // removeChimSelection(chimObj.getChimeraModel().getChain(
+    // ((ChimeraResidue) chimObj).getChainId()));
+    // }
+    // }
+    // } else {
+    // removeChimSelection(chimObj);
+    // if (chimObj.hasSelectedChildren() && chimObj instanceof ChimeraModel) {
+    // for (ChimeraResidue residue : ((ChimeraModel) chimObj)
+    // .getSelectedResidues()) {
+    // removeChimSelection(residue);
+    // }
+    // }
+    // }
+    // }
+    // }
+    // System.out.println("selection list: " + getChimSelectionCount());
+    updateChimeraSelection();
+    selectionChanged();
+  }
+
+  // Save models in a HashMap/Set for better performance?
+  public void updateChimeraSelection()
+  {
+    // System.out.println("update Chimera selection");
+    String selSpec = "";
+    for (int i = 0; i < chimSelectionList.size(); i++)
+    {
+      ChimeraStructuralObject nodeInfo = chimSelectionList.get(i);
+      // we do not care about the model anymore
+      selSpec = selSpec.concat(nodeInfo.toSpec());
+      if (i < chimSelectionList.size() - 1)
+        selSpec.concat("|");
+    }
+    if (selSpec.length() > 0)
+    {
+      chimeraManager.select("sel " + selSpec);
+    }
+    else
+    {
+      chimeraManager.select("~sel");
+    }
+  }
+
+  /**
+   * This is called by the selectionListener to let us know that the user has
+   * changed their selection in Chimera. We need to go back to Chimera to find
+   * out what is currently selected and update our list.
+   */
+  public void chimeraSelectionChanged()
+  {
+    // System.out.println("Chimera selection changed");
+    clearSelectionList();
+    // Execute the command to get the list of models with selections
+    Map<Integer, ChimeraModel> selectedModelsMap = chimeraManager
+            .getSelectedModels();
+    // Now get the residue-level data
+    chimeraManager.getSelectedResidues(selectedModelsMap);
+    // Get the selected objects
+    try
+    {
+      for (ChimeraModel selectedModel : selectedModelsMap.values())
+      {
+        int modelNumber = selectedModel.getModelNumber();
+        int subModelNumber = selectedModel.getSubModelNumber();
+        // Get the corresponding "real" model
+        if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber))
+        {
+          ChimeraModel dataModel = chimeraManager.getChimeraModel(
+                  modelNumber, subModelNumber);
+          if (dataModel.getResidueCount() == selectedModel
+                  .getResidueCount()
+                  || dataModel.getModelType() == StructureManager.ModelType.SMILES)
+          {
+            // Select the entire model
+            addChimSelection(dataModel);
+            // dataModel.setSelected(true);
+          }
+          else
+          {
+            for (ChimeraChain selectedChain : selectedModel.getChains())
+            {
+              ChimeraChain dataChain = dataModel.getChain(selectedChain
+                      .getChainId());
+              if (selectedChain.getResidueCount() == dataChain
+                      .getResidueCount())
+              {
+                addChimSelection(dataChain);
+                // dataChain.setSelected(true);
+              }
+              // else {
+              // Need to select individual residues
+              for (ChimeraResidue res : selectedChain.getResidues())
+              {
+                String residueIndex = res.getIndex();
+                ChimeraResidue residue = dataChain.getResidue(residueIndex);
+                if (residue == null)
+                {
+                  continue;
+                }
+                addChimSelection(residue);
+                // residue.setSelected(true);
+              } // resIter.hasNext
+                // }
+            } // chainIter.hasNext()
+          }
+        }
+      } // modelIter.hasNext()
+    } catch (Exception ex)
+    {
+      logger.warn("Could not update selection", ex);
+    }
+    // System.out.println("selection list: " + getChimSelectionCount());
+    // Finally, update the navigator panel
+    selectionChanged();
+    updateCytoscapeSelection();
+  }
+
+  public void selectFunctResidues(Collection<ChimeraModel> models)
+  {
+    clearSelectionList();
+    for (ChimeraModel model : models)
+    {
+      for (ChimeraResidue residue : model.getFuncResidues())
+      {
+        addChimSelection(residue);
+      }
+    }
+    updateChimeraSelection();
+    updateCytoscapeSelection();
+    selectionChanged();
+  }
+
+  // public void selectFunctResidues(CyNode node, CyNetwork network) {
+  // clearSelectionList();
+  // if (currentCyMap.containsKey(node)) {
+  // Set<ChimeraStructuralObject> chimObjects = currentCyMap.get(node);
+  // for (ChimeraStructuralObject obj : chimObjects) {
+  // if (obj instanceof ChimeraModel) {
+  // ChimeraModel model = (ChimeraModel) obj;
+  // for (ChimeraResidue residue : model.getFuncResidues()) {
+  // addChimSelection(residue);
+  // }
+  // }
+  // }
+  // }
+  // updateChimeraSelection();
+  // updateCytoscapeSelection();
+  // selectionChanged();
+  // }
+
+  public List<ChimeraStructuralObject> getChimSelectionList()
+  {
+    return chimSelectionList;
+  }
+
+  public int getChimSelectionCount()
+  {
+    return chimSelectionList.size();
+  }
+
+  /**
+   * Add a selection to the selection list. This is called primarily by the
+   * Model Navigator Dialog to keep the selections in sync
+   * 
+   * @param selectionToAdd
+   *          the selection to add to our list
+   */
+  public void addChimSelection(ChimeraStructuralObject selectionToAdd)
+  {
+    if (selectionToAdd != null
+            && !chimSelectionList.contains(selectionToAdd))
+    {
+      chimSelectionList.add(selectionToAdd);
+      selectionToAdd.setSelected(true);
+    }
+  }
+
+  /**
+   * Remove a selection from the selection list. This is called primarily by the
+   * Model Navigator Dialog to keep the selections in sync
+   * 
+   * @param selectionToRemove
+   *          the selection to remove from our list
+   */
+  public void removeChimSelection(ChimeraStructuralObject selectionToRemove)
+  {
+    if (selectionToRemove != null
+            && chimSelectionList.contains(selectionToRemove))
+    {
+      chimSelectionList.remove(selectionToRemove);
+      selectionToRemove.setSelected(false);
+    }
+  }
+
+  /**
+   * Clear the list of selected objects
+   */
+  public void clearSelectionList()
+  {
+    for (ChimeraStructuralObject cso : chimSelectionList)
+    {
+      if (cso != null)
+        cso.setSelected(false);
+    }
+    chimSelectionList.clear();
+  }
+
+  /**
+   * Associate a new network with the corresponding Chimera objects.
+   * 
+   * @param network
+   */
+
+  /**
+   * Dump and refresh all of our model/chain/residue info
+   */
+  public void updateModels()
+  {
+    // Stop all of our listeners while we try to handle this
+    chimeraManager.stopListening();
+
+    // Get all of the open models
+    List<ChimeraModel> newModelList = chimeraManager.getModelList();
+
+    // Match them up -- assume that the model #'s haven't changed
+    for (ChimeraModel newModel : newModelList)
+    {
+      // Get the color (for our navigator)
+      newModel.setModelColor(chimeraManager.getModelColor(newModel));
+
+      // Get our model info
+      int modelNumber = newModel.getModelNumber();
+      int subModelNumber = newModel.getSubModelNumber();
+
+      // If we already know about this model number, get the Structure,
+      // which tells us about the associated CyNode
+      if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber))
+      {
+        ChimeraModel oldModel = chimeraManager.getChimeraModel(modelNumber,
+                subModelNumber);
+        chimeraManager.removeChimeraModel(modelNumber, subModelNumber);
+        newModel.setModelType(oldModel.getModelType());
+        if (oldModel.getModelType() == ModelType.SMILES)
+        {
+          newModel.setModelName(oldModel.getModelName());
+        }
+        // re-assign associations to cytoscape objects
+        // Map<CyIdentifiable, CyNetwork> oldModelCyObjs =
+        // oldModel.getCyObjects();
+        // for (CyIdentifiable cyObj : oldModelCyObjs.keySet()) {
+        // // add cy objects to the new model
+        // newModel.addCyObject(cyObj, oldModelCyObjs.get(cyObj));
+        // if (currentCyMap.containsKey(cyObj)) {
+        // currentCyMap.get(cyObj).add(newModel);
+        // if (currentCyMap.get(cyObj).contains(oldModel)) {
+        // currentCyMap.get(cyObj).remove(oldModel);
+        // }
+        // }
+        // }
+        // // add new model to the chimera objects map and remove old model
+        // if (currentChimMap.containsKey(oldModel)) {
+        // currentChimMap.put(newModel, currentChimMap.get(oldModel));
+        // currentChimMap.remove(oldModel);
+        // }
+      }
+      // add new model to ChimeraManager
+      chimeraManager.addChimeraModel(modelNumber, subModelNumber, newModel);
+
+      // Get the residue information
+      if (newModel.getModelType() != ModelType.SMILES)
+      {
+        chimeraManager.addResidues(newModel);
+      }
+      // for (CyIdentifiable cyObj : newModel.getCyObjects().keySet()) {
+      // if (cyObj != null && cyObj instanceof CyNetwork) {
+      // addStructureNetwork((CyNetwork) cyObj);
+      // } else if (cyObj != null && cyObj instanceof CyNode) {
+      // newModel.setFuncResidues(ChimUtils.parseFuncRes(
+      // getResidueList(newModel.getCyObjects().get(cyObj), cyObj),
+      // newModel.getModelName()));
+      // }
+      // }
+    }
+
+    // associate all models with any node or network
+    // aTask.associate();
+
+    // Restart all of our listeners
+    chimeraManager.startListening();
+    // Done
+  }
+
+  public void launchModelNavigatorDialog()
+  {
+    // TODO: [Optional] Use haveGUI flag
+    // if (!haveGUI) {
+    // return;
+    // }
+    // if (mnDialog == null) {
+    // CySwingApplication cyApplication = (CySwingApplication)
+    // getService(CySwingApplication.class);
+    // mnDialog = new ModelNavigatorDialog(cyApplication.getJFrame(), this);
+    // mnDialog.pack();
+    // }
+    // mnDialog.setVisible(true);
+  }
+
+  public boolean isMNDialogOpen()
+  {
+    // if (mnDialog != null && mnDialog.isVisible()) {
+    // return true;
+    // }
+    return false;
+  }
+
+  /**
+   * Invoked by the listener thread.
+   */
+  public void modelChanged()
+  {
+    // if (mnDialog != null) {
+    // mnDialog.modelChanged();
+    // }
+  }
+
+  /**
+   * Inform our interface that the selection has changed
+   */
+  public void selectionChanged()
+  {
+    // if (mnDialog != null) {
+    // // System.out.println("update dialog selection");
+    // mnDialog.updateSelection(new
+    // ArrayList<ChimeraStructuralObject>(chimSelectionList));
+    // }
+  }
+
+  public void launchAlignDialog(boolean useChains)
+  {
+    // TODO: [Optional] Use haveGUI flag
+    // Sometimes it does not appear in Windows
+    // if (!haveGUI) {
+    // return;
+    // }
+    // if (alDialog != null) {
+    // alDialog.setVisible(false);
+    // alDialog.dispose();
+    // }
+    // System.out.println("launch align dialog");
+    List<ChimeraStructuralObject> chimObjectList = new ArrayList<ChimeraStructuralObject>();
+    for (ChimeraModel model : chimeraManager.getChimeraModels())
+    {
+      if (useChains)
+      {
+        for (ChimeraChain chain : model.getChains())
+        {
+          chimObjectList.add(chain);
+        }
+      }
+      else
+      {
+        chimObjectList.add(model);
+      }
+    }
+    // Bring up the dialog
+    // CySwingApplication cyApplication = (CySwingApplication)
+    // getService(CySwingApplication.class);
+    // alDialog = new AlignStructuresDialog(cyApplication.getJFrame(), this,
+    // chimObjectList);
+    // alDialog.pack();
+    // alDialog.setVisible(true);
+  }
+
+  public List<String> getAllStructureKeys()
+  {
+    return Arrays.asList(defaultStructureKeys);
+  }
+
+  public List<String> getAllChemStructKeys()
+  {
+    return Arrays.asList(defaultChemStructKeys);
+  }
+
+  public List<String> getAllResidueKeys()
+  {
+    return Arrays.asList(defaultResidueKeys);
+  }
+
+  public List<String> getAllChimeraResidueAttributes()
+  {
+    List<String> attributes = new ArrayList<String>();
+    // attributes.addAll(rinManager.getResAttrs());
+    attributes.addAll(chimeraManager.getAttrList());
+    return attributes;
+  }
+
+  StructureSettings defaultSettings = null;
+
+  // TODO: [Optional] Change priority of Chimera paths
+  public List<String> getChimeraPaths()
+  {
+    List<String> pathList = new ArrayList<String>();
+
+    // if no network is available and the settings have been modified by the
+    // user, check for a
+    // path to chimera
+    if (defaultSettings != null)
+    {
+      String defaultPath = defaultSettings.getChimeraPath();
+      if (defaultPath != null && !defaultPath.equals(""))
+      {
+        pathList.add(defaultPath);
+        return pathList;
+      }
+    }
+
+    // if no network settings, check if the last chimera path is saved in the
+    // session
+    // String lastPath = CytoUtils.getDefaultChimeraPath(registrar,
+    // chimeraPropertyName,
+    // chimeraPathPropertyKey);
+    // if (lastPath != null && !lastPath.equals("")) {
+    // pathList.add(lastPath);
+    // return pathList;
+    // }
+
+    // if no user settings and no last path, get default system's settings
+    String os = System.getProperty("os.name");
+    if (os.startsWith("Linux"))
+    {
+      pathList.add("/usr/local/chimera/bin/chimera");
+      pathList.add("/usr/local/bin/chimera");
+      pathList.add("/usr/bin/chimera");
+    }
+    else if (os.startsWith("Windows"))
+    {
+      pathList.add("\\Program Files\\Chimera\\bin\\chimera");
+      pathList.add("C:\\Program Files\\Chimera\\bin\\chimera.exe");
+    }
+    else if (os.startsWith("Mac"))
+    {
+      pathList.add("/Applications/Chimera.app/Contents/MacOS/chimera");
+    }
+    return pathList;
+  }
+
+  public void setChimeraPathProperty(String path)
+  {
+    // CytoUtils.setDefaultChimeraPath(registrar, chimeraPropertyName,
+    // chimeraPathPropertyKey,
+    // path);
+  }
+
+  public void setStructureSettings(StructureSettings structureSettings)
+  {
+    this.defaultSettings = structureSettings;
+  }
+
+  public String getCurrentChimeraPath(Object object)
+  {
+    if (defaultSettings != null)
+    {
+      return defaultSettings.getChimeraPath();
+    }
+    else
+    {
+      return "";
+    }
+  }
+
+  // public void initChimTable() {
+  // CyTableManager manager = (CyTableManager) getService(CyTableManager.class);
+  // CyTableFactory factory = (CyTableFactory) getService(CyTableFactory.class);
+  // for (CyTable table : manager.getGlobalTables()) {
+  // if (table.getTitle().equals(chimeraOutputTable)) {
+  // manager.deleteTable(table.getSUID());
+  // }
+  // }
+  // chimTable = factory.createTable(chimeraOutputTable, chimeraCommandAttr,
+  // String.class,
+  // false, true);
+  // manager.addTable(chimTable);
+  // if (chimTable.getColumn(chimeraOutputAttr) == null) {
+  // chimTable.createListColumn(chimeraOutputAttr, String.class, false);
+  // }
+  // }
+
+  // public void addChimReply(String command, List<String> reply) {
+  // chimTable.getRow(command).set(chimeraOutputAttr, reply);
+  // }
+
+}