Merge branch 'develop' into features/JAL-2295setChimeraAttributes
[jalview.git] / src / jalview / ext / rbvi / chimera / JalviewChimeraBinding.java
index 72b33cc..75ddc9c 100644 (file)
@@ -23,15 +23,17 @@ package jalview.ext.rbvi.chimera;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
+import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.httpserver.AbstractRequestHandler;
+import jalview.io.DataSourceType;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ResidueProperties;
 import jalview.structure.AtomSpec;
@@ -117,6 +119,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    */
   private long loadNotifiesHandled = 0;
 
+  private Thread chimeraMonitor;
+
   /**
    * Open a PDB structure file in Chimera and set up mappings from Jalview.
    * 
@@ -197,11 +201,41 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    * @param protocol
    */
   public JalviewChimeraBinding(StructureSelectionManager ssm,
-          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String protocol)
+          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, DataSourceType protocol)
   {
     super(ssm, pdbentry, sequenceIs, protocol);
-    viewer = new ChimeraManager(
-            new ext.edu.ucsf.rbvi.strucviz2.StructureManager(true));
+    viewer = new ChimeraManager(new StructureManager(true));
+  }
+
+  /**
+   * Starts a thread that waits for the Chimera process to finish, so that we
+   * can then close the associated resources. This avoids leaving orphaned
+   * Chimera viewer panels in Jalview if the user closes Chimera.
+   */
+  protected void startChimeraProcessMonitor()
+  {
+    final Process p = viewer.getChimeraProcess();
+    chimeraMonitor = new Thread(new Runnable()
+    {
+
+      @Override
+      public void run()
+      {
+        try
+        {
+          p.waitFor();
+          JalviewStructureDisplayI display = getViewer();
+          if (display != null)
+          {
+            display.closeViewer(false);
+          }
+        } catch (InterruptedException e)
+        {
+          // exit thread if Chimera Viewer is closed in Jalview
+        }
+      }
+    });
+    chimeraMonitor.start();
   }
 
   /**
@@ -288,6 +322,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     }
     viewer = null;
 
+    if (chimeraMonitor != null)
+    {
+      chimeraMonitor.interrupt();
+    }
     releaseUIResources();
   }
 
@@ -568,23 +606,29 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   /**
    * Launch Chimera, unless an instance linked to this object is already
-   * running. Returns true if chimera is successfully launched, or already
+   * running. Returns true if Chimera is successfully launched, or already
    * running, else false.
    * 
    * @return
    */
   public boolean launchChimera()
   {
-    if (!viewer.isChimeraLaunched())
-    {
-      return viewer.launchChimera(StructureManager.getChimeraPaths());
-    }
     if (viewer.isChimeraLaunched())
     {
       return true;
     }
-    log("Failed to launch Chimera!");
-    return false;
+
+    boolean launched = viewer.launchChimera(StructureManager
+            .getChimeraPaths());
+    if (launched)
+    {
+      startChimeraProcessMonitor();
+    }
+    else
+    {
+      log("Failed to launch Chimera!");
+    }
+    return launched;
   }
 
   /**
@@ -1175,19 +1219,32 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
     String cmd = "list residues attr '" + attName + "'";
     List<String> residues = sendChimeraCommand(cmd, true);
-    /*
-     * Expect 0, 1 or more reply lines of the format (chi2 is attName):
-     * residue id #0:5.A chi2 -155.000836316 index 5
-     * or
-     * residue id #0:6.A chi3 None
-     * 
-     * We assume here that attributes on structure do not naturally convert
-     * to ranges on sequence, i.e. we just set one feature per mapped position.
-     * 
-     * To conflate positions, would need to first build a map 
-     * Map<String value, Map<Sequence seq, List<Integer position>>>
-     * and then traverse it to find feature ranges.
-     */
+
+    boolean featureAdded = createFeaturesForAttributes(attName, residues);
+    if (featureAdded)
+    {
+      alignmentPanel.getFeatureRenderer().featuresAdded();
+    }
+  }
+
+  /**
+   * Create features in Jalview for the given attribute name and structure
+   * residues.
+   * 
+   * <pre>
+   * The residue list should be 0, 1 or more reply lines of the format: 
+   *     residue id #0:5.A isHelix -155.000836316 index 5 
+   * or 
+   *     residue id #0:6.A isHelix None
+   * </pre>
+   * 
+   * @param attName
+   * @param residues
+   * @return
+   */
+  protected boolean createFeaturesForAttributes(String attName,
+          List<String> residues)
+  {
     boolean featureAdded = false;
     String featureGroup = getViewerFeatureGroup();
 
@@ -1236,6 +1293,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       spec.setPdbFile(pdbFile);
 
       List<AtomSpec> atoms = Collections.singletonList(spec);
+
+      /*
+       * locate the mapped position in the alignment (if any)
+       */
       SearchResults sr = getSsm()
               .findAlignmentPositionsForStructurePositions(atoms);
 
@@ -1243,7 +1304,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
        * expect one matched alignment position, or none 
        * (if the structure position is not mapped)
        */
-      for (Match m : sr.getResults())
+      for (SearchResultMatchI m : sr.getResults())
       {
         SequenceI seq = m.getSequence();
         int start = m.getStart();
@@ -1255,10 +1316,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
         featureAdded |= seq.addSequenceFeature(sf);
       }
     }
-    if (featureAdded)
-    {
-      alignmentPanel.getFeatureRenderer().featuresAdded();
-    }
+    return featureAdded;
   }
 
   /**