JAL-1620 version bump and release notes
[jalview.git] / src / jalview / ext / rbvi / chimera / JalviewChimeraBinding.java
index 7e72d8f..9b9c239 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
  * Copyright (C) 2014 The Jalview Authors
  * 
  * This file is part of Jalview.
@@ -20,7 +20,6 @@
  */
 package jalview.ext.rbvi.chimera;
 
-import static org.junit.Assert.assertTrue;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
@@ -37,42 +36,32 @@ import jalview.structure.StructureListener;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.SequenceStructureBindingModel;
+import jalview.util.MessageManager;
 
 import java.awt.Color;
-import java.awt.Container;
 import java.awt.event.ComponentEvent;
-import java.awt.event.ComponentListener;
 import java.io.File;
-import java.net.URL;
-import java.security.AccessControlException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.Hashtable;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Vector;
-
-import org.jmol.adapter.smarter.SmarterJmolAdapter;
-import org.jmol.api.JmolAppConsoleInterface;
-import org.jmol.api.JmolSelectionListener;
-import org.jmol.api.JmolStatusListener;
-import org.jmol.api.JmolViewer;
-import org.jmol.constant.EnumCallback;
-import org.jmol.popup.JmolPopup;
 
 import ext.edu.ucsf.rbvi.strucviz2.ChimeraManager;
 import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel;
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
-import sun.rmi.runtime.Log;
 
 public abstract class JalviewChimeraBinding extends
         SequenceStructureBindingModel implements StructureListener,
         SequenceStructureBinding, StructureSelectionManagerProvider
 
 {
+  private static final String PHOSPHORUS = "P";
+
+  private static final String ALPHACARBON = "CA";
+
   private StructureManager csm;
 
   private ChimeraManager viewer;
@@ -85,13 +74,14 @@ public abstract class JalviewChimeraBinding extends
   private boolean loadingFromArchive = false;
 
   /**
-   * second flag to indicate if the jmol viewer should ignore sequence colouring
-   * events from the structure manager because the GUI is still setting up
+   * second flag to indicate if the Chimera viewer should ignore sequence
+   * colouring events from the structure manager because the GUI is still
+   * setting up
    */
   private boolean loadingFinished = true;
 
   /**
-   * state flag used to check if the Jmol viewer's paint method can be called
+   * state flag used to check if the Chimera viewer's paint method can be called
    */
   private boolean finishedInit = false;
 
@@ -109,18 +99,18 @@ public abstract class JalviewChimeraBinding extends
 
   /**
    * when true, try to search the associated datamodel for sequences that are
-   * associated with any unknown structures in the Jmol view.
+   * associated with any unknown structures in the Chimera view.
    */
   private boolean associateNewStructs = false;
 
-  Vector atomsPicked = new Vector();
+  List<String> atomsPicked = new ArrayList<String>();
 
-  public Vector chainNames;
+  public List<String> chainNames;
 
-  Hashtable chainFile;
+  private Map<String, String> chainFile;
 
   /**
-   * array of target chains for seuqences - tied to pdbentry and sequence[]
+   * array of target chains for sequences - tied to pdbentry and sequence[]
    */
   protected String[][] chains;
 
@@ -130,7 +120,7 @@ public abstract class JalviewChimeraBinding extends
 
   public String fileLoadingError;
 
-  private Map<String, List<ChimeraModel>> chimmaps = new HashMap<String, List<ChimeraModel>>();
+  private Map<String, List<ChimeraModel>> chimmaps = new LinkedHashMap<String, List<ChimeraModel>>();
 
   private List<String> mdlToFile = new ArrayList<String>();
 
@@ -152,7 +142,7 @@ public abstract class JalviewChimeraBinding extends
     try
     {
       List<ChimeraModel> oldList = viewer.getModelList();
-      viewer.openModel(file, ModelType.PDB_MODEL);
+      viewer.openModel(file, pe.getId(), ModelType.PDB_MODEL);
       List<ChimeraModel> newList = viewer.getModelList();
       if (oldList.size() < newList.size())
       {
@@ -164,7 +154,7 @@ public abstract class JalviewChimeraBinding extends
         chimmaps.put(file, newList);
         for (ChimeraModel cm : newList)
         {
-          while (mdlToFile.size()<1+cm.getModelNumber())
+          while (mdlToFile.size() < 1 + cm.getModelNumber())
           {
             mdlToFile.add(new String(""));
           }
@@ -184,7 +174,7 @@ public abstract class JalviewChimeraBinding extends
         } catch (Error e)
         {
         }
-        // Explicitly map to the filename used by Jmol ;
+        // Explicitly map to the filename used by Chimera ;
         // pdbentry[pe].getFile(), protocol);
 
         if (ssm != null)
@@ -247,13 +237,6 @@ public abstract class JalviewChimeraBinding extends
     }
     viewer = new ChimeraManager(
             csm = new ext.edu.ucsf.rbvi.strucviz2.StructureManager(true));
-    /*
-     * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
-     * "jalviewJmol", ap.av.applet .getDocumentBase(),
-     * ap.av.applet.getCodeBase(), "", this);
-     * 
-     * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
-     */
   }
 
   public JalviewChimeraBinding(StructureSelectionManager ssm,
@@ -265,12 +248,13 @@ public abstract class JalviewChimeraBinding extends
   }
 
   /**
-   * construct a title string for the viewer window based on the data jalview
+   * Construct a title string for the viewer window based on the data Jalview
    * knows about
    * 
+   * @param verbose
    * @return
    */
-  public String getViewerTitle()
+  public String getViewerTitle(boolean verbose)
   {
     if (sequence == null || pdbentry == null || sequence.length < 1
             || pdbentry.length < 1 || sequence[0].length < 1)
@@ -279,20 +263,24 @@ public abstract class JalviewChimeraBinding extends
     }
     // TODO: give a more informative title when multiple structures are
     // displayed.
-    StringBuffer title = new StringBuffer("Chimera view for "
-            + sequence[0][0].getName() + ":" + pdbentry[0].getId());
+    StringBuilder title = new StringBuilder(64);
+    title.append("Chimera view for " + sequence[0][0].getName() + ":"
+            + pdbentry[0].getId());
 
-    if (pdbentry[0].getProperty() != null)
+    if (verbose)
     {
-      if (pdbentry[0].getProperty().get("method") != null)
+      if (pdbentry[0].getProperty() != null)
       {
-        title.append(" Method: ");
-        title.append(pdbentry[0].getProperty().get("method"));
-      }
-      if (pdbentry[0].getProperty().get("chains") != null)
-      {
-        title.append(" Chain:");
-        title.append(pdbentry[0].getProperty().get("chains"));
+        if (pdbentry[0].getProperty().get("method") != null)
+        {
+          title.append(" Method: ");
+          title.append(pdbentry[0].getProperty().get("method"));
+        }
+        if (pdbentry[0].getProperty().get("chains") != null)
+        {
+          title.append(" Chain:");
+          title.append(pdbentry[0].getProperty().get("chains"));
+        }
       }
     }
     return title.toString();
@@ -302,47 +290,52 @@ public abstract class JalviewChimeraBinding extends
    * prepare the view for a given set of models/chains. chainList contains
    * strings of the form 'pdbfilename:Chaincode'
    * 
-   * @param chainList
+   * @param toshow
    *          list of chains to make visible
    */
-  public void centerViewer(Vector chainList)
+  public void centerViewer(List<String> toshow)
   {
-    StringBuffer cmd = new StringBuffer();
-    String lbl;
+    StringBuilder cmd = new StringBuilder(64);
     int mlength, p;
-    for (int i = 0, iSize = chainList.size(); i < iSize; i++)
+    for (String lbl : toshow)
     {
       mlength = 0;
-      lbl = (String) chainList.elementAt(i);
       do
       {
         p = mlength;
         mlength = lbl.indexOf(":", p);
       } while (p < mlength && mlength < (lbl.length() - 2));
       // TODO: lookup each pdb id and recover proper model number for it.
-      cmd.append("#" + getModelNum((String) chainFile.get(lbl)) + "."
+      cmd.append("#" + getModelNum(chainFile.get(lbl)) + "."
               + lbl.substring(mlength + 1) + " or ");
     }
     if (cmd.length() > 0)
+    {
       cmd.setLength(cmd.length() - 4);
-    evalStateCommand("~display #*; ~ribbon #*; ribbon " + cmd + ";focus "
-            + cmd);
+    }
+    String cmdstring = cmd.toString();
+    evalStateCommand("~display #*; ~ribbon #*; ribbon " + cmdstring
+            + ";focus " + cmdstring, false);
   }
 
-  public void closeViewer()
+  /**
+   * Close down the Jalview viewer, and (optionally) the associate Chimera
+   * window.
+   */
+  public void closeViewer(boolean closeChimera)
   {
     ssm.removeStructureViewerListener(this, this.getPdbFile());
-    // and shut down Chimera
-    viewer.exitChimera();
-    // viewer.evalStringQuiet("zap");
-    // viewer.setJmolStatusListener(null);
+    if (closeChimera)
+    {
+      viewer.exitChimera();
+    }
     lastCommand = null;
     viewer = null;
     releaseUIResources();
   }
 
   /**
-   * called by JalviewJmolbinding after closeViewer is called - release any
+   * called by JalviewChimerabinding after closeViewer is called - release any
    * resources and references so they can be garbage collected.
    */
   protected abstract void releaseUIResources();
@@ -350,17 +343,15 @@ public abstract class JalviewChimeraBinding extends
   public void colourByChain()
   {
     colourBySequence = false;
-    // TODO: colour by chain should colour each chain distinctly across all
-    // visible models
-    // TODO: http://issues.jalview.org/browse/JAL-628
-    evalStateCommand("select *;color chain");
+    evalStateCommand("rainbow chain", false);
   }
 
   public void colourByCharge()
   {
     colourBySequence = false;
-    evalStateCommand("colour *;color white;select ASP,GLU;color red;"
-            + "select LYS,ARG;color blue;select CYS;color yellow");
+    evalStateCommand(
+            "color white;color red ::ASP;color red ::GLU;color blue ::LYS;color blue ::ARG;color yellow ::CYS",
+            false);
   }
 
   /**
@@ -408,9 +399,9 @@ public abstract class JalviewChimeraBinding extends
           int[] _refStructure, ColumnSelection[] _hiddenCols)
   {
     assert (_alignment.length == _refStructure.length && _alignment.length != _hiddenCols.length);
-
+    StringBuilder allComs = new StringBuilder(128); // Chimera superposition cmd
     String[] files = getPdbFile();
-    // check to see if we are still waiting for Jmol files
+    // check to see if we are still waiting for Chimera files
     long starttime = System.currentTimeMillis();
     boolean waiting = true;
     do
@@ -441,26 +432,11 @@ public abstract class JalviewChimeraBinding extends
     if (waiting)
     {
       System.err
-              .println("RUNTIME PROBLEM: Jmol seems to be taking a long time to process all the structures.");
+              .println("RUNTIME PROBLEM: Chimera seems to be taking a long time to process all the structures.");
       return;
     }
+    refreshPdbEntries();
     StringBuffer selectioncom = new StringBuffer();
-    // In principle - nSeconds specifies the speed of animation for each
-    // superposition - but is seems to behave weirdly, so we don't specify it.
-    String nSeconds = " ";
-    if (files.length > 10)
-    {
-      nSeconds = " 0.00001 ";
-    }
-    else
-    {
-      nSeconds = " " + (2.0 / files.length) + " ";
-      // if (nSeconds).substring(0,5)+" ";
-    }
-    // see JAL-1345 - should really automatically turn off the animation for
-    // large numbers of structures, but Jmol doesn't seem to allow that.
-    nSeconds = " ";
-    // union of all aligned positions are collected together.
     for (int a = 0; a < _alignment.length; a++)
     {
       int refStructure = _refStructure[a];
@@ -469,9 +445,9 @@ public abstract class JalviewChimeraBinding extends
       if (a > 0
               && selectioncom.length() > 0
               && !selectioncom.substring(selectioncom.length() - 1).equals(
-                      "|"))
+                      " "))
       {
-        selectioncom.append("|");
+        selectioncom.append(" ");
       }
       // process this alignment
       if (refStructure >= files.length)
@@ -484,7 +460,6 @@ public abstract class JalviewChimeraBinding extends
       {
         refStructure = -1;
       }
-      StringBuffer command = new StringBuffer();
 
       boolean matched[] = new boolean[alignment.getWidth()];
       for (int m = 0; m < matched.length; m++)
@@ -495,9 +470,9 @@ public abstract class JalviewChimeraBinding extends
 
       int commonrpositions[][] = new int[files.length][alignment.getWidth()];
       String isel[] = new String[files.length];
-      // reference structure - all others are superposed in it
       String[] targetC = new String[files.length];
       String[] chainNames = new String[files.length];
+      String[] atomSpec = new String[files.length];
       for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
       {
         StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
@@ -505,8 +480,7 @@ public abstract class JalviewChimeraBinding extends
         // Jmol callback has completed.
         if (mapping == null || mapping.length < 1)
         {
-          throw new Error(
-                  "Implementation error - Jmol seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016");
+          throw new Error(MessageManager.getString("error.implementation_error_chimera_getting_data"));
         }
         int lastPos = -1;
         for (int s = 0; s < sequence[pdbfnum].length; s++)
@@ -553,7 +527,7 @@ public abstract class JalviewChimeraBinding extends
                 commonrpositions[pdbfnum][r] = pos;
               }
               // create model selection suffix
-              isel[pdbfnum] = "/" + (pdbfnum + 1) + ".1";
+              isel[pdbfnum] = "#" + pdbfnum;
               if (mapping[m].getChain() == null
                       || mapping[m].getChain().trim().length() == 0)
               {
@@ -561,10 +535,11 @@ public abstract class JalviewChimeraBinding extends
               }
               else
               {
-                targetC[pdbfnum] = ":" + mapping[m].getChain();
+                targetC[pdbfnum] = "." + mapping[m].getChain();
               }
               chainNames[pdbfnum] = mapping[m].getPdbId()
                       + targetC[pdbfnum];
+              atomSpec[pdbfnum] = asp.getRNA() != null ? PHOSPHORUS : ALPHACARBON;
               // move on to next pdb file
               s = sequence[pdbfnum].length;
               break;
@@ -581,6 +556,7 @@ public abstract class JalviewChimeraBinding extends
 
       String[] selcom = new String[files.length];
       int nmatched = 0;
+      String sep = "";
       // generate select statements to select regions to superimpose structures
       {
         for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
@@ -589,7 +565,6 @@ public abstract class JalviewChimeraBinding extends
           int lpos = -1;
           boolean run = false;
           StringBuffer molsel = new StringBuffer();
-          molsel.append("{");
           for (int r = 0; r < matched.length; r++)
           {
             if (matched[r])
@@ -603,10 +578,9 @@ public abstract class JalviewChimeraBinding extends
                 // discontinuity
                 if (lpos != -1)
                 {
-                  molsel.append(lpos);
+                  molsel.append((run ? "" : ":") + lpos);
                   molsel.append(chainCd);
-                  // molsel.append("} {");
-                  molsel.append("|");
+                  molsel.append(",");
                 }
               }
               else
@@ -615,7 +589,7 @@ public abstract class JalviewChimeraBinding extends
                 if (!run)
                 {
                   // at the beginning, so add dash
-                  molsel.append(lpos);
+                  molsel.append(":" + lpos);
                   molsel.append("-");
                 }
                 run = true;
@@ -627,22 +601,19 @@ public abstract class JalviewChimeraBinding extends
           // add final selection phrase
           if (lpos != -1)
           {
-            molsel.append(lpos);
+            molsel.append((run ? "" : ":") + lpos);
             molsel.append(chainCd);
-            molsel.append("}");
+            // molsel.append("");
           }
           if (molsel.length() > 1)
           {
             selcom[pdbfnum] = molsel.toString();
-            selectioncom.append("((");
-            selectioncom.append(selcom[pdbfnum].substring(1,
-                    selcom[pdbfnum].length() - 1));
-            selectioncom.append(" )& ");
-            selectioncom.append(pdbfnum + 1);
-            selectioncom.append(".1)");
+            selectioncom.append("#" + pdbfnum);
+            selectioncom.append(selcom[pdbfnum]);
+            selectioncom.append(" ");
             if (pdbfnum < files.length - 1)
             {
-              selectioncom.append("|");
+              selectioncom.append("| ");
             }
           }
           else
@@ -651,6 +622,7 @@ public abstract class JalviewChimeraBinding extends
           }
         }
       }
+      StringBuilder command = new StringBuilder(256);
       for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
       {
         if (pdbfnum == refStructure || selcom[pdbfnum] == null
@@ -658,36 +630,37 @@ public abstract class JalviewChimeraBinding extends
         {
           continue;
         }
-        command.append("echo ");
-        command.append("\"Superposing (");
-        command.append(chainNames[pdbfnum]);
-        command.append(") against reference (");
-        command.append(chainNames[refStructure]);
-        command.append(")\";\ncompare " + nSeconds);
-        command.append("{");
-        command.append(1 + pdbfnum);
-        command.append(".1} {");
-        command.append(1 + refStructure);
-        command.append(".1} SUBSET {*.CA | *.P} ATOMS ");
-
-        // form the matched pair strings
-        String sep = "";
-        for (int s = 0; s < 2; s++)
+        if (command.length() > 0)
         {
-          command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
+          command.append(";");
         }
-        command.append(" ROTATE TRANSLATE;\n");
+
+        /*
+         * Form Chimera match command, from the 'new' structure to the
+         * 'reference' structure e.g. (residues 1-91, chain B/A, alphacarbons):
+         * 
+         * match #1:1-91.B@CA #0:1-91.A@CA
+         * 
+         * @see
+         * https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/midas/match.html
+         */
+        command.append("match #" + pdbfnum /* +".1" */);
+        // TODO: handle sub-models
+        command.append(selcom[pdbfnum]);
+        command.append("@" + atomSpec[pdbfnum]);
+        command.append(" #" + refStructure /* +".1" */);
+        command.append(selcom[refStructure]);
+        command.append("@" + atomSpec[refStructure]);
       }
       if (selectioncom.length() > 0)
       {
+        // TODO remove debug output
         System.out.println("Select regions:\n" + selectioncom.toString());
-        evalStateCommand("select *; cartoons off; backbone; select ("
-                + selectioncom.toString() + "); cartoons; ");
-        // selcom.append("; ribbons; ");
         System.out
                 .println("Superimpose command(s):\n" + command.toString());
-
-        evalStateCommand(command.toString());
+        allComs.append("~display all; chain @CA|P; ribbon "
+                + selectioncom.toString() + ";"+command.toString());
+        // selcom.append("; ribbons; ");
       }
     }
     if (selectioncom.length() > 0)
@@ -697,10 +670,12 @@ public abstract class JalviewChimeraBinding extends
         selectioncom.setLength(selectioncom.length() - 1);
       }
       System.out.println("Select regions:\n" + selectioncom.toString());
-      evalStateCommand("select *; cartoons off; backbone; select ("
-              + selectioncom.toString() + "); cartoons; ");
+      allComs.append("; ~display all; chain @CA|P; ribbon "
+              + selectioncom.toString() + "; focus");
       // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
+      evalStateCommand(allComs.toString(), true /* false */);
     }
+    
   }
 
   private void checkLaunched()
@@ -715,18 +690,51 @@ public abstract class JalviewChimeraBinding extends
     }
   }
 
-  public void evalStateCommand(String command)
+  /**
+   * Answers true if the Chimera process is still running, false if ended or not
+   * started.
+   * 
+   * @return
+   */
+  public boolean isChimeraRunning()
+  {
+    return viewer.isChimeraLaunched();
+  }
+
+  /**
+   * Send a command to Chimera, and optionally log any responses.
+   * 
+   * @param command
+   * @param logResponse
+   */
+  public void evalStateCommand(final String command, boolean logResponse)
   {
     viewerCommandHistory(false);
     checkLaunched();
     if (lastCommand == null || !lastCommand.equals(command))
     {
-
-      lastReply = viewer.sendChimeraCommand(command, true);
-      if (debug)
-      {
-        log("Response from command ('" + command + "') was:\n" + lastReply);
-      }
+//      Thread t = new Thread(new Runnable()
+//      {
+//        @Override
+//        public void run()
+//        {
+      // trim command or it may never find a match in the replyLog!!
+      lastReply = viewer.sendChimeraCommand(command.trim(), logResponse);
+      if (debug && logResponse)
+          {
+            log("Response from command ('" + command + "') was:\n"
+                    + lastReply);
+          }
+//        }
+//      });
+      // TODO - use j7/8 thread management
+//      try
+//      {
+//        t.join();
+//      } catch (InterruptedException foo)
+//      {
+//      }
+//      ;
     }
     viewerCommandHistory(true);
     lastCommand = command;
@@ -741,7 +749,9 @@ public abstract class JalviewChimeraBinding extends
           jalview.api.AlignmentViewPanel alignmentv)
   {
     if (!colourBySequence || !loadingFinished)
+    {
       return;
+    }
     if (ssm == null)
     {
       return;
@@ -760,10 +770,25 @@ public abstract class JalviewChimeraBinding extends
     for (jalview.structure.StructureMappingcommandSet cpdbbyseq : ChimeraCommands
             .getColourBySequenceCommand(ssm, files, sequence, sr, fr,
                     alignment))
+    {
       for (String cbyseq : cpdbbyseq.commands)
       {
-        evalStateCommand(cbyseq);
+        waitForChimera();
+        evalStateCommand(cbyseq, false);
+        waitForChimera();
       }
+    }
+  }
+
+  private void waitForChimera()
+  {
+    while (viewer != null && viewer.isBusy())
+    {
+      try {
+        Thread.sleep(15);
+      } catch (InterruptedException q)
+      {}
+    }
   }
 
   public boolean isColourBySequence()
@@ -776,25 +801,6 @@ public abstract class JalviewChimeraBinding extends
     this.colourBySequence = colourBySequence;
   }
 
-  public void createImage(String file, String type, int quality)
-  {
-    System.out.println("JMOL CREATE IMAGE");
-  }
-
-  public String createImage(String fileName, String type,
-          Object textOrBytes, int quality)
-  {
-    System.out.println("JMOL CREATE IMAGE");
-    return null;
-  }
-
-  public String eval(String strEval)
-  {
-    // System.out.println(strEval);
-    // "# 'eval' is implemented only for the applet.";
-    return null;
-  }
-
   // End StructureListener
   // //////////////////////////
 
@@ -813,7 +819,9 @@ public abstract class JalviewChimeraBinding extends
           String pdbfile)
   {
     if (getModelNum(pdbfile) < 0)
+    {
       return null;
+    }
     log("get model / residue colour attribute unimplemented");
     return null;
   }
@@ -846,7 +854,9 @@ public abstract class JalviewChimeraBinding extends
     for (int i = 0; i < mfn.length; i++)
     {
       if (mfn[i].equalsIgnoreCase(modelFileName))
+      {
         return i;
+      }
     }
     return -1;
   }
@@ -866,18 +876,18 @@ public abstract class JalviewChimeraBinding extends
     {
       return new String[0];
     }
-//    if (modelFileNames == null)
-//    {
-//      Collection<ChimeraModel> chimodels = viewer.getChimeraModels();
-//      _modelFileNameMap = new int[chimodels.size()];
-//      int j = 0;
-//      for (ChimeraModel chimodel : chimodels)
-//      {
-//        String mdlName = chimodel.getModelName();
-//      }
-//      modelFileNames = new String[j];
-//      // System.arraycopy(mset, 0, modelFileNames, 0, j);
-//    }
+    // if (modelFileNames == null)
+    // {
+    // Collection<ChimeraModel> chimodels = viewer.getChimeraModels();
+    // _modelFileNameMap = new int[chimodels.size()];
+    // int j = 0;
+    // for (ChimeraModel chimodel : chimodels)
+    // {
+    // String mdlName = chimodel.getModelName();
+    // }
+    // modelFileNames = new String[j];
+    // // System.arraycopy(mset, 0, modelFileNames, 0, j);
+    // }
 
     return chimmaps.keySet().toArray(
             modelFileNames = new String[chimmaps.size()]);
@@ -913,20 +923,20 @@ public abstract class JalviewChimeraBinding extends
       int mdlNum = cms.get(0).getModelNumber();
 
       viewerCommandHistory(false);
-      viewer.stopListening();
+      // viewer.stopListening();
       if (resetLastRes.length() > 0)
       {
-        viewer.sendChimeraCommand(resetLastRes.toString(), false);
+        eval.setLength(0);
+        eval.append(resetLastRes.toString() + ";");
       }
 
-      eval.setLength(0);
       eval.append("display "); // +modelNum
 
       resetLastRes.setLength(0);
       resetLastRes.append("~display ");
       {
         eval.append(" #" + (mdlNum));
-        resetLastRes.append("#" + (mdlNum));
+        resetLastRes.append(" #" + (mdlNum));
       }
       // complete select string
 
@@ -937,18 +947,14 @@ public abstract class JalviewChimeraBinding extends
         eval.append("." + chain);
         resetLastRes.append("." + chain);
       }
-      eval.append(eval.toString());
-
-      resetLastRes.append(resetLastRes.toString()
-              );
-
+      
       viewer.sendChimeraCommand(eval.toString(), false);
       viewerCommandHistory(true);
-      viewer.startListening();
+      // viewer.startListening();
     }
   }
 
-  boolean debug = true;
+  boolean debug = false;
 
   private void log(String message)
   {
@@ -957,8 +963,8 @@ public abstract class JalviewChimeraBinding extends
 
   private void viewerCommandHistory(boolean enable)
   {
-    log("(Not yet implemented) History "
-            + ((debug || enable) ? "on" : "off"));
+    // log("(Not yet implemented) History "
+    // + ((debug || enable) ? "on" : "off"));
   }
 
   public void loadInline(String string)
@@ -1009,8 +1015,10 @@ public abstract class JalviewChimeraBinding extends
     String chainId;
 
     if (strInfo.indexOf(":") > -1)
+    {
       chainId = strInfo.substring(strInfo.indexOf(":") + 1,
               strInfo.indexOf("."));
+    }
     else
     {
       chainId = " ";
@@ -1048,7 +1056,9 @@ public abstract class JalviewChimeraBinding extends
       ;
     }
     if (lastMessage == null || !lastMessage.equals(strInfo))
+    {
       ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
+    }
 
     lastMessage = strInfo;
   }
@@ -1067,13 +1077,17 @@ public abstract class JalviewChimeraBinding extends
     int chainSeparator = strInfo.indexOf(":");
     int p = 0;
     if (chainSeparator == -1)
+    {
       chainSeparator = strInfo.indexOf(".");
+    }
 
     String picked = strInfo.substring(strInfo.indexOf("]") + 1,
             chainSeparator);
     String mdlString = "";
     if ((p = strInfo.indexOf(":")) > -1)
+    {
       picked += strInfo.substring(p + 1, strInfo.indexOf("."));
+    }
 
     if ((p = strInfo.indexOf("/")) > -1)
     {
@@ -1086,12 +1100,12 @@ public abstract class JalviewChimeraBinding extends
     if (!atomsPicked.contains(picked))
     {
       viewer.select(picked);
-      atomsPicked.addElement(picked);
+      atomsPicked.add(picked);
     }
     else
     {
       viewer.select("not " + picked);
-      atomsPicked.removeElement(picked);
+      atomsPicked.remove(picked);
     }
     viewerCommandHistory(true);
     // TODO: in application this happens
@@ -1106,7 +1120,7 @@ public abstract class JalviewChimeraBinding extends
 
   // incremented every time a load notification is successfully handled -
   // lightweight mechanism for other threads to detect when they can start
-  // referrring to new structures.
+  // referring to new structures.
   private long loadNotifiesHandled = 0;
 
   public long getLoadNotifiesHandled()
@@ -1132,8 +1146,8 @@ public abstract class JalviewChimeraBinding extends
     fileLoadingError = null;
     String[] oldmodels = modelFileNames;
     modelFileNames = null;
-    chainNames = new Vector();
-    chainFile = new Hashtable();
+    chainNames = new ArrayList<String>();
+    chainFile = new HashMap<String, String>();
     boolean notifyLoaded = false;
     String[] modelfilenames = getPdbFile();
     // first check if we've lost any structures
@@ -1194,49 +1208,43 @@ public abstract class JalviewChimeraBinding extends
     colourBySequence = false;
 
     if (cs == null)
+    {
       return;
+    }
 
     String res;
     int index;
     Color col;
+    // Chimera expects RBG values in the range 0-1
+    final double normalise = 255D;
     viewerCommandHistory(false);
     // TODO: Switch between nucleotide or aa selection expressions
     Enumeration en = ResidueProperties.aa3Hash.keys();
-    StringBuffer command = new StringBuffer("select *;color white;");
+    StringBuilder command = new StringBuilder(128);
+    command.append("color white;");
     while (en.hasMoreElements())
     {
       res = en.nextElement().toString();
       index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
       if (index > 20)
+      {
         continue;
+      }
 
       col = cs.findColour(ResidueProperties.aa[index].charAt(0));
-      // TODO: need colour string function and res selection here
-      command.append("select " + res + ";color[" + col.getRed() + ","
-              + col.getGreen() + "," + col.getBlue() + "];");
+      command.append("color " + col.getRed() / normalise + ","
+              + col.getGreen() / normalise + "," + col.getBlue()
+              / normalise + " ::" + res + ";");
     }
 
-    evalStateCommand(command.toString());
+    evalStateCommand(command.toString(),false);
     viewerCommandHistory(true);
   }
 
-  public void showHelp()
-  {
-    // chimera help
-    showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
-  }
-
-  /**
-   * open the URL somehow
-   * 
-   * @param target
-   */
-  public abstract void showUrl(String url, String target);
-
   /**
-   * called when the binding thinks the UI needs to be refreshed after a Jmol
+   * called when the binding thinks the UI needs to be refreshed after a Chimera
    * state change. this could be because structures were loaded, or because an
-   * error has occured.
+   * error has occurred.
    */
   public abstract void refreshGUI();
 
@@ -1265,8 +1273,8 @@ public abstract class JalviewChimeraBinding extends
 
   /**
    * 
-   * @return true if Jmol is still restoring state or loading is still going on
-   *         (see setFinsihedLoadingFromArchive)
+   * @return true if Chimeral is still restoring state or loading is still going
+   *         on (see setFinsihedLoadingFromArchive)
    */
   public boolean isLoadingFromArchive()
   {
@@ -1284,13 +1292,22 @@ public abstract class JalviewChimeraBinding extends
     loadingFinished = finishedLoading;
   }
 
-  public void setBackgroundColour(java.awt.Color col)
+  /**
+   * Send the Chimera 'background solid <color>" command.
+   * 
+   * @see https
+   *      ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/background
+   *      .html
+   * @param col
+   */
+  public void setBackgroundColour(Color col)
   {
     viewerCommandHistory(false);
-    // todo set background colour
-    viewer.sendChimeraCommand(
-            "background [" + col.getRed() + "," + col.getGreen() + ","
-                    + col.getBlue() + "];", false);
+    double normalise = 255D;
+    final String command = "background solid " + col.getRed() / normalise + ","
+            + col.getGreen() / normalise + "," + col.getBlue()
+            / normalise + ";";
+    viewer.sendChimeraCommand(command, false);
     viewerCommandHistory(true);
   }
 
@@ -1302,21 +1319,20 @@ public abstract class JalviewChimeraBinding extends
   public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
           SequenceI[][] seq, String[][] chns)
   {
-    int pe = -1;
-    Vector v = new Vector();
-    Vector rtn = new Vector();
+    List<PDBEntry> v = new ArrayList<PDBEntry>();
+    List<int[]> rtn = new ArrayList<int[]>();
     for (int i = 0; i < pdbentry.length; i++)
     {
-      v.addElement(pdbentry[i]);
+      v.add(pdbentry[i]);
     }
     for (int i = 0; i < pdbe.length; i++)
     {
       int r = v.indexOf(pdbe[i]);
       if (r == -1 || r >= pdbentry.length)
       {
-        rtn.addElement(new int[]
+        rtn.add(new int[]
         { v.size(), i });
-        v.addElement(pdbe[i]);
+        v.add(pdbe[i]);
       }
       else
       {
@@ -1324,12 +1340,11 @@ public abstract class JalviewChimeraBinding extends
         addSequenceAndChain(r, seq[i], chns[i]);
       }
     }
-    pdbe = new PDBEntry[v.size()];
-    v.copyInto(pdbe);
+    pdbe = v.toArray(new PDBEntry[v.size()]);
     pdbentry = pdbe;
     if (rtn.size() > 0)
     {
-      // expand the tied seuqence[] and string[] arrays
+      // expand the tied sequence[] and string[] arrays
       SequenceI[][] sqs = new SequenceI[pdbentry.length][];
       String[][] sch = new String[pdbentry.length][];
       System.arraycopy(sequence, 0, sqs, 0, sequence.length);
@@ -1339,7 +1354,7 @@ public abstract class JalviewChimeraBinding extends
       pdbe = new PDBEntry[rtn.size()];
       for (int r = 0; r < pdbe.length; r++)
       {
-        int[] stri = ((int[]) rtn.elementAt(r));
+        int[] stri = (rtn.get(r));
         // record the pdb file as a new addition
         pdbe[r] = pdbentry[stri[0]];
         // and add the new sequence/chain entries
@@ -1353,9 +1368,14 @@ public abstract class JalviewChimeraBinding extends
     return pdbe;
   }
 
+  /**
+   * Adds sequences to the pe'th pdbentry's sequence set.
+   * 
+   * @param pe
+   * @param seq
+   */
   public void addSequence(int pe, SequenceI[] seq)
   {
-    // add sequences to the pe'th pdbentry's seuqence set.
     addSequenceAndChain(pe, seq, null);
   }
 
@@ -1363,13 +1383,14 @@ public abstract class JalviewChimeraBinding extends
   {
     if (pe < 0 || pe >= pdbentry.length)
     {
-      throw new Error(
-              "Implementation error - no corresponding pdbentry (for index "
-                      + pe + ") to add sequences mappings to");
+      throw new Error(MessageManager.formatMessage(
+              "error.implementation_error_no_pdbentry_from_index",
+              new Object[]
+              { Integer.valueOf(pe).toString() }));
     }
     final String nullChain = "TheNullChain";
-    Vector s = new Vector();
-    Vector c = new Vector();
+    List<SequenceI> s = new ArrayList<SequenceI>();
+    List<String> c = new ArrayList<String>();
     if (chains == null)
     {
       chains = new String[pdbentry.length][];
@@ -1378,23 +1399,23 @@ public abstract class JalviewChimeraBinding extends
     {
       for (int i = 0; i < sequence[pe].length; i++)
       {
-        s.addElement(sequence[pe][i]);
+        s.add(sequence[pe][i]);
         if (chains[pe] != null)
         {
           if (i < chains[pe].length)
           {
-            c.addElement(chains[pe][i]);
+            c.add(chains[pe][i]);
           }
           else
           {
-            c.addElement(nullChain);
+            c.add(nullChain);
           }
         }
         else
         {
           if (tchain != null && tchain.length > 0)
           {
-            c.addElement(nullChain);
+            c.add(nullChain);
           }
         }
       }
@@ -1403,20 +1424,18 @@ public abstract class JalviewChimeraBinding extends
     {
       if (!s.contains(seq[i]))
       {
-        s.addElement(seq[i]);
+        s.add(seq[i]);
         if (tchain != null && i < tchain.length)
         {
-          c.addElement(tchain[i] == null ? nullChain : tchain[i]);
+          c.add(tchain[i] == null ? nullChain : tchain[i]);
         }
       }
     }
-    SequenceI[] tmp = new SequenceI[s.size()];
-    s.copyInto(tmp);
+    SequenceI[] tmp = s.toArray(new SequenceI[s.size()]);
     sequence[pe] = tmp;
     if (c.size() > 0)
     {
-      String[] tch = new String[c.size()];
-      c.copyInto(tch);
+      String[] tch = c.toArray(new String[c.size()]);
       for (int i = 0; i < tch.length; i++)
       {
         if (tch[i] == nullChain)