JAL-3949 Complete new abstracted logging framework in jalview.log. Updated log calls...
[jalview.git] / src / jalview / ext / jmol / JalviewJmolBinding.java
index b6e6a65..f133f20 100644 (file)
  */
 package jalview.ext.jmol;
 
-import jalview.api.FeatureRenderer;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.gui.IProgressIndicator;
-import jalview.io.DataSourceType;
-import jalview.io.StructureFile;
-import jalview.structure.AtomSpec;
-import jalview.structure.StructureSelectionManager;
-import jalview.structures.models.AAStructureBindingModel;
-import jalview.util.MessageManager;
-
 import java.awt.Container;
 import java.awt.event.ComponentEvent;
 import java.awt.event.ComponentListener;
 import java.io.File;
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
+import javax.swing.SwingUtilities;
+
 import org.jmol.adapter.smarter.SmarterJmolAdapter;
 import org.jmol.api.JmolAppConsoleInterface;
 import org.jmol.api.JmolSelectionListener;
@@ -53,6 +41,26 @@ import org.jmol.api.JmolViewer;
 import org.jmol.c.CBK;
 import org.jmol.viewer.Viewer;
 
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureRenderer;
+import jalview.api.FeatureSettingsModelI;
+import jalview.api.SequenceRenderer;
+import jalview.bin.Cache;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AppJmol;
+import jalview.gui.IProgressIndicator;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.DataSourceType;
+import jalview.io.StructureFile;
+import jalview.structure.AtomSpec;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureSelectionManager;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.ws.dbsources.Pdb;
+import javajs.util.BS;
+
 public abstract class JalviewJmolBinding extends AAStructureBindingModel
         implements JmolStatusListener, JmolSelectionListener,
         ComponentListener
@@ -83,8 +91,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     setStructureCommands(new JmolCommands());
     /*
      * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
-     * "jalviewJmol", ap.av.applet .getDocumentBase(),
-     * ap.av.applet.getCodeBase(), "", this);
+     * "jalviewJmol", ap.av.applet .getDocumentBase(), ap.av.applet.getCodeBase(),
+     * "", this);
      * 
      * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
      */
@@ -112,314 +120,31 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     return getViewerTitle("Jmol", true);
   }
 
-  public void closeViewer()
-  {
-    // remove listeners for all structures in viewer
-    getSsm().removeStructureViewerListener(this, this.getStructureFiles());
-    jmolViewer.dispose();
-    lastCommand = null;
-    jmolViewer = null;
-    releaseUIResources();
-  }
-
-  /**
-   * superpose the structures associated with sequences in the alignment
-   * according to their corresponding positions.
-   * 
-   * @deprecated not used - remove?
-   */
-  @Deprecated
-  public void superposeStructures(AlignmentI alignment)
-  {
-    superposeStructures(alignment, -1, null);
-  }
-
-  /**
-   * superpose the structures associated with sequences in the alignment
-   * according to their corresponding positions. ded)
-   * 
-   * @param refStructure
-   *          - select which pdb file to use as reference (default is -1 - the
-   *          first structure in the alignment)
-   * @deprecated not used - remove?
-   */
-  @Deprecated
-  public void superposeStructures(AlignmentI alignment, int refStructure)
+  private String jmolScript(String script)
   {
-    superposeStructures(alignment, refStructure, null);
-  }
+    Cache.debug(">>Jmol>> " + script);
+    String s = jmolViewer.evalStringQuiet(script); // scriptWait(script); BH
+    Cache.debug("<<Jmol<< " + s);
 
-  /**
-   * superpose the structures associated with sequences in the alignment
-   * according to their corresponding positions. ded)
-   * 
-   * @param refStructure
-   *          - select which pdb file to use as reference (default is -1 - the
-   *          first structure in the alignment)
-   * @param hiddenCols
-   *          TODO
-   * @deprecated not used - remove?
-   */
-  @Deprecated
-  public void superposeStructures(AlignmentI alignment, int refStructure,
-          HiddenColumns hiddenCols)
-  {
-    superposeStructures(new AlignmentI[] { alignment },
-            new int[]
-            { refStructure }, new HiddenColumns[] { hiddenCols });
+    return s;
   }
 
-  /**
-   * {@inheritDoc}
-   */
   @Override
-  public String superposeStructures(AlignmentI[] _alignment,
-          int[] _refStructure, HiddenColumns[] _hiddenCols)
-  {
-    while (jmolViewer.isScriptExecuting())
-    {
-      try
-      {
-        Thread.sleep(10);
-      } catch (InterruptedException i)
-      {
-      }
-    }
-
-    /*
-     * get the distinct structure files modelled
-     * (a file with multiple chains may map to multiple sequences)
-     */
-    String[] files = getStructureFiles();
-    if (!waitForFileLoad(files))
-    {
-      return null;
-    }
-
-    StringBuilder selectioncom = new StringBuilder(256);
-    // 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.005 ";
-    }
-    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];
-      AlignmentI alignment = _alignment[a];
-      HiddenColumns hiddenCols = _hiddenCols[a];
-      if (a > 0 && selectioncom.length() > 0 && !selectioncom
-              .substring(selectioncom.length() - 1).equals("|"))
-      {
-        selectioncom.append("|");
-      }
-      // process this alignment
-      if (refStructure >= files.length)
-      {
-        System.err.println(
-                "Invalid reference structure value " + refStructure);
-        refStructure = -1;
-      }
-
-      /*
-       * 'matched' bit j will be set for visible alignment columns j where
-       * all sequences have a residue with a mapping to the PDB structure
-       */
-      BitSet matched = new BitSet();
-      for (int m = 0; m < alignment.getWidth(); m++)
-      {
-        if (hiddenCols == null || hiddenCols.isVisible(m))
-        {
-          matched.set(m);
-        }
-      }
-
-      SuperposeData[] structures = new SuperposeData[files.length];
-      for (int f = 0; f < files.length; f++)
-      {
-        structures[f] = new SuperposeData(alignment.getWidth());
-      }
-
-      /*
-       * Calculate the superposable alignment columns ('matched'), and the
-       * corresponding structure residue positions (structures.pdbResNo)
-       */
-      int candidateRefStructure = findSuperposableResidues(alignment,
-              matched, structures);
-      if (refStructure < 0)
-      {
-        /*
-         * If no reference structure was specified, pick the first one that has
-         * a mapping in the alignment
-         */
-        refStructure = candidateRefStructure;
-      }
-
-      String[] selcom = new String[files.length];
-      int nmatched = matched.cardinality();
-      if (nmatched < 4)
-      {
-        return (MessageManager.formatMessage("label.insufficient_residues",
-                nmatched));
-      }
-
-      /*
-       * generate select statements to select regions to superimpose structures
-       */
-      {
-        // TODO extract method to construct selection statements
-        for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
-        {
-          String chainCd = ":" + structures[pdbfnum].chain;
-          int lpos = -1;
-          boolean run = false;
-          StringBuilder molsel = new StringBuilder();
-          molsel.append("{");
-
-          int nextColumnMatch = matched.nextSetBit(0);
-          while (nextColumnMatch != -1)
-          {
-            int pdbResNo = structures[pdbfnum].pdbResNo[nextColumnMatch];
-            if (lpos != pdbResNo - 1)
-            {
-              // discontinuity
-              if (lpos != -1)
-              {
-                molsel.append(lpos);
-                molsel.append(chainCd);
-                molsel.append("|");
-              }
-              run = false;
-            }
-            else
-            {
-              // continuous run - and lpos >-1
-              if (!run)
-              {
-                // at the beginning, so add dash
-                molsel.append(lpos);
-                molsel.append("-");
-              }
-              run = true;
-            }
-            lpos = pdbResNo;
-            nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1);
-          }
-          /*
-           * add final selection phrase
-           */
-          if (lpos != -1)
-          {
-            molsel.append(lpos);
-            molsel.append(chainCd);
-            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)");
-            if (pdbfnum < files.length - 1)
-            {
-              selectioncom.append("|");
-            }
-          }
-          else
-          {
-            selcom[pdbfnum] = null;
-          }
-        }
-      }
-      StringBuilder command = new StringBuilder(256);
-      // command.append("set spinFps 10;\n");
-
-      for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
-      {
-        if (pdbfnum == refStructure || selcom[pdbfnum] == null
-                || selcom[refStructure] == null)
-        {
-          continue;
-        }
-        command.append("echo ");
-        command.append("\"Superposing (");
-        command.append(structures[pdbfnum].pdbId);
-        command.append(") against reference (");
-        command.append(structures[refStructure].pdbId);
-        command.append(")\";\ncompare " + nSeconds);
-        command.append("{");
-        command.append(Integer.toString(1 + pdbfnum));
-        command.append(".1} {");
-        command.append(Integer.toString(1 + refStructure));
-        // conformation=1 excludes alternate locations for CA (JAL-1757)
-        command.append(
-                ".1} SUBSET {(*.CA | *.P) and conformation=1} ATOMS ");
-
-        // for (int s = 0; s < 2; s++)
-        // {
-        // command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
-        // }
-        command.append(selcom[pdbfnum]);
-        command.append(selcom[refStructure]);
-        command.append(" ROTATE TRANSLATE;\n");
-      }
-      if (selectioncom.length() > 0)
-      {
-        // TODO is performing selectioncom redundant here? is done later on
-        // System.out.println("Select regions:\n" + selectioncom.toString());
-        executeCommand("select *; cartoons off; backbone; select ("
-                + selectioncom.toString() + "); cartoons; ", false);
-        // selcom.append("; ribbons; ");
-        String cmdString = command.toString();
-        // System.out.println("Superimpose command(s):\n" + cmdString);
-
-        executeCommand(cmdString, false);
-      }
-    }
-    if (selectioncom.length() > 0)
-    {// finally, mark all regions that were superposed.
-      if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
-      {
-        selectioncom.setLength(selectioncom.length() - 1);
-      }
-      // System.out.println("Select regions:\n" + selectioncom.toString());
-      executeCommand("select *; cartoons off; backbone; select ("
-              + selectioncom.toString() + "); cartoons; ", false);
-      // evalStateCommand("select *; backbone; select "+selcom.toString()+";
-      // cartoons; center "+selcom.toString());
-    }
-
-    return null;
-  }
-
-  @Override
-  public List<String> executeCommand(String command, boolean getReply)
+  public List<String> executeCommand(StructureCommandI command,
+          boolean getReply)
   {
     if (command == null)
     {
       return null;
     }
+    String cmd = command.getCommand();
     jmolHistory(false);
-    if (lastCommand == null || !lastCommand.equals(command))
+    if (lastCommand == null || !lastCommand.equals(cmd))
     {
-      jmolViewer.evalStringQuiet(command + "\n");
+      jmolScript(cmd + "\n");
     }
     jmolHistory(true);
-    lastCommand = command;
+    lastCommand = cmd;
     return null;
   }
 
@@ -471,7 +196,6 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   @Override
   public synchronized String[] getStructureFiles()
   {
-    List<String> mset = new ArrayList<>();
     if (jmolViewer == null)
     {
       return new String[0];
@@ -481,15 +205,23 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     {
       int modelCount = jmolViewer.ms.mc;
       String filePath = null;
+      List<String> mset = new ArrayList<>();
       for (int i = 0; i < modelCount; ++i)
       {
+        /*
+         * defensive check for null as getModelFileName can return null even when model
+         * count ms.mc is > 0
+         */
         filePath = jmolViewer.ms.getModelFileName(i);
-        if (!mset.contains(filePath))
+        if (filePath != null && !mset.contains(filePath))
         {
           mset.add(filePath);
         }
       }
-      modelFileNames = mset.toArray(new String[mset.size()]);
+      if (!mset.isEmpty())
+      {
+        modelFileNames = mset.toArray(new String[mset.size()]);
+      }
     }
 
     return modelFileNames;
@@ -524,7 +256,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     {
       if (resetLastRes.length() > 0)
       {
-        jmolViewer.evalStringQuiet(resetLastRes.toString());
+        jmolScript(resetLastRes.toString());
         resetLastRes.setLength(0);
       }
       for (AtomSpec atom : atoms)
@@ -539,59 +271,39 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
           String pdbfile)
   {
-    if (modelFileNames == null)
-    {
-      return;
-    }
-
-    // look up file model number for this pdbfile
-    int mdlNum = 0;
-    // may need to adjust for URLencoding here - we don't worry about that yet.
-    while (mdlNum < modelFileNames.length
-            && !pdbfile.equals(modelFileNames[mdlNum]))
-    {
-      mdlNum++;
-    }
-    if (mdlNum == modelFileNames.length)
+    String modelId = getModelIdForFile(pdbfile);
+    if (modelId.isEmpty())
     {
       return;
     }
 
     jmolHistory(false);
 
+    StringBuilder selection = new StringBuilder(32);
     StringBuilder cmd = new StringBuilder(64);
-    cmd.append("select ").append(String.valueOf(pdbResNum)); // +modelNum
-
-    resetLastRes.append("select ").append(String.valueOf(pdbResNum)); // +modelNum
-
-    cmd.append(":");
-    resetLastRes.append(":");
+    selection.append("select ").append(String.valueOf(pdbResNum));
+    selection.append(":");
     if (!chain.equals(" "))
     {
-      cmd.append(chain);
-      resetLastRes.append(chain);
-    }
-    {
-      cmd.append(" /").append(String.valueOf(mdlNum + 1));
-      resetLastRes.append("/").append(String.valueOf(mdlNum + 1));
+      selection.append(chain);
     }
-    cmd.append(";wireframe 100;" + cmd.toString() + " and not hetero;");
+    selection.append(" /").append(modelId);
 
-    resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
-            + " and not hetero; spacefill 0;");
+    cmd.append(selection).append(";wireframe 100;").append(selection)
+            .append(" and not hetero;").append("spacefill 200;select none");
 
-    cmd.append("spacefill 200;select none");
+    resetLastRes.append(selection).append(";wireframe 0;").append(selection)
+            .append(" and not hetero; spacefill 0;");
 
-    jmolViewer.evalStringQuiet(cmd.toString());
+    jmolScript(cmd.toString());
     jmolHistory(true);
-
   }
 
   private boolean debug = true;
 
   private void jmolHistory(boolean enable)
   {
-    jmolViewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
+    jmolScript("History " + ((debug || enable) ? "on" : "off"));
   }
 
   public void loadInline(String string)
@@ -691,8 +403,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     }
 
     /*
-     * highlight position on alignment(s); if some text is returned, 
-     * show this as a second line on the structure hover tooltip
+     * highlight position on alignment(s); if some text is returned, show this as a
+     * second line on the structure hover tooltip
      */
     String label = getSsm().mouseOverStructure(pdbResNum, chainId,
             pdbfilename);
@@ -707,7 +419,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       sb.append(";set hoverLabel \"").append(toks.nextToken()).append(" ")
               .append(toks.nextToken());
       sb.append("|").append(label).append("\"");
-      executeCommand(sb.toString(), false);
+      executeCommand(new StructureCommand(sb.toString()), false);
     }
   }
 
@@ -728,8 +440,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   /*
    * { if (history != null && strStatus != null &&
-   * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
-   * } }
+   * !strStatus.equals("Script completed")) { history.append("\n" + strStatus); }
+   * }
    */
 
   public void notifyAtomPicked(int atomIndex, String strInfo,
@@ -737,7 +449,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   {
     /**
      * this implements the toggle label behaviour copied from the original
-     * structure viewer, MCView
+     * structure viewer, mc_view
      */
     if (strData != null)
     {
@@ -768,7 +480,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
     if (!atomsPicked.contains(picked))
     {
-      jmolViewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
+      jmolScript("select " + picked + ";label %n %r:%c");
       atomsPicked.addElement(picked);
     }
     else
@@ -790,6 +502,28 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   @Override
   public void notifyCallback(CBK type, Object[] data)
   {
+    /*
+     * ensure processed in AWT thread to avoid risk of deadlocks
+     */
+    SwingUtilities.invokeLater(new Runnable()
+    {
+
+      @Override
+      public void run()
+      {
+        processCallback(type, data);
+      }
+    });
+  }
+
+  /**
+   * Processes one callback notification from Jmol
+   * 
+   * @param type
+   * @param data
+   */
+  protected void processCallback(CBK type, Object[] data)
+  {
     try
     {
       switch (type)
@@ -891,6 +625,11 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     modelFileNames = null;
     boolean notifyLoaded = false;
     String[] modelfilenames = getStructureFiles();
+    if (modelfilenames == null)
+    {
+      // Jmol is still loading files!
+      return;
+    }
     // first check if we've lost any structures
     if (oldmodels != null && oldmodels.length > 0)
     {
@@ -954,8 +693,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
           // see JAL-623 - need method of matching pasted data up
           {
             pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
-                    pdbfile, DataSourceType.PASTE,
-                    getIProgressIndicator());
+                    pdbfile, DataSourceType.PASTE, getIProgressIndicator());
             getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
             matches = true;
             foundEntry = true;
@@ -994,14 +732,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
         }
         if (matches)
         {
-          // add an entry for every chain in the model
-          for (int i = 0; i < pdb.getChains().size(); i++)
-          {
-            String chid = pdb.getId() + ":"
-                    + pdb.getChains().elementAt(i).id;
-            addChainFile(chid, fileName);
-            getChainNames().add(chid);
-          }
+          stashFoundChains(pdb, fileName);
           notifyLoaded = true;
         }
       }
@@ -1030,7 +761,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     // }
     if (!isLoadingFromArchive())
     {
-      jmolViewer.evalStringQuiet(
+      jmolScript(
               "model *; select backbone;restrict;cartoon;wireframe off;spacefill off");
     }
     // register ourselves as a listener and notify the gui that it needs to
@@ -1041,7 +772,9 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       FeatureRenderer fr = getFeatureRenderer(null);
       if (fr != null)
       {
-        fr.featuresAdded();
+        FeatureSettingsModelI colours = new Pdb().getFeatureColourScheme();
+        ((AppJmol) getViewer()).getAlignmentPanel().av
+                .applyFeaturesStyle(colours);
       }
       refreshGUI();
       loadNotifiesHandled++;
@@ -1070,8 +803,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   public abstract void sendConsoleEcho(String strEcho); /*
                                                          * { showConsole(true);
                                                          * 
-                                                         * history.append("\n" +
-                                                         * strEcho); }
+                                                         * history.append("\n" + strEcho); }
                                                          */
 
   // /End JmolStatusListener
@@ -1095,7 +827,9 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   public void showHelp()
   {
-    showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
+    showUrl("http://wiki.jmol.org"
+    // BH 2018 "http://jmol.sourceforge.net/docs/JmolUserGuide/"
+            , "jmolHelp");
   }
 
   /**
@@ -1112,7 +846,16 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
    */
   public abstract void showConsole(boolean show);
 
+  public static Viewer getJmolData(JmolParser jmolParser)
+  {
+    return (Viewer) JmolViewer.allocateViewer(null, null, null, null, null,
+            "-x -o -n", jmolParser);
+  }
+
   /**
+   * 
+   * 
+   * 
    * @param renderPanel
    * @param jmolfileio
    *          - when true will initialise jmol's file IO system (should be false
@@ -1143,13 +886,16 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
    * @param consolePanel
    *          - panel to contain Jmol console
    * @param buttonsToShow
-   *          - buttons to show on the console, in ordr
+   *          - buttons to show on the console, in order
    */
   public void allocateViewer(Container renderPanel, boolean jmolfileio,
           String htmlName, URL documentBase, URL codeBase,
           String commandOptions, final Container consolePanel,
           String buttonsToShow)
   {
+
+    System.err.println("Allocating Jmol Viewer: " + commandOptions);
+
     if (commandOptions == null)
     {
       commandOptions = "";
@@ -1161,7 +907,15 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
     jmolViewer.setJmolStatusListener(this); // extends JmolCallbackListener
 
-    console = createJmolConsole(consolePanel, buttonsToShow);
+    try
+    {
+      console = createJmolConsole(consolePanel, buttonsToShow);
+    } catch (Throwable e)
+    {
+      System.err.println("Could not create Jmol application console. "
+              + e.getMessage());
+      e.printStackTrace();
+    }
     if (consolePanel != null)
     {
       consolePanel.addComponentListener(this);
@@ -1173,6 +927,9 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   protected abstract JmolAppConsoleInterface createJmolConsole(
           Container consolePanel, String buttonsToShow);
 
+  // BH 2018 -- Jmol console is not working due to problems with styled
+  // documents.
+
   protected org.jmol.api.JmolAppConsoleInterface console = null;
 
   @Override
@@ -1237,20 +994,62 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   }
 
   @Override
-  protected int getModelNoForFile(String pdbFile)
+  protected String getModelIdForFile(String pdbFile)
   {
     if (modelFileNames == null)
     {
-      return -1;
+      return "";
     }
     for (int i = 0; i < modelFileNames.length; i++)
     {
       if (modelFileNames[i].equalsIgnoreCase(pdbFile))
       {
-        return i;
+        return String.valueOf(i + 1);
       }
     }
-    return -1;
+    return "";
+  }
+
+  @Override
+  protected ViewerType getViewerType()
+  {
+    return ViewerType.JMOL;
+  }
 
+  @Override
+  protected String getModelId(int pdbfnum, String file)
+  {
+    return String.valueOf(pdbfnum + 1);
+  }
+
+  /**
+   * Returns ".spt" - the Jmol session file extension
+   * 
+   * @return
+   * @see https://chemapps.stolaf.edu/jmol/docs/#writemodel
+   */
+  @Override
+  public String getSessionFileExtension()
+  {
+    return ".spt";
+  }
+
+  @Override
+  public void selectionChanged(BS arg0)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  @Override
+  public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
+  {
+    return new jalview.gui.SequenceRenderer(avp.getAlignViewport());
+  }
+
+  @Override
+  public String getHelpURL()
+  {
+    return "http://wiki.jmol.org"; // BH 2018
   }
 }