upgrading structure/sequence binding so that a structure view can contain many pdb...
[jalview.git] / src / jalview / gui / AppJmol.java
index 32fc85f..dcca874 100644 (file)
@@ -1,20 +1,19 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
- * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
+ * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
  * 
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * This file is part of Jalview.
  * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  */
 package jalview.gui;
 
@@ -27,6 +26,8 @@ import java.awt.event.*;
 import java.io.*;
 
 import jalview.jbgui.GStructureViewer;
+import jalview.api.SequenceStructureBinding;
+import jalview.bin.Cache;
 import jalview.datamodel.*;
 import jalview.gui.*;
 import jalview.structure.*;
@@ -38,9 +39,10 @@ import jalview.ws.ebi.EBIFetchClient;
 import org.jmol.api.*;
 import org.jmol.adapter.smarter.SmarterJmolAdapter;
 import org.jmol.popup.*;
+import org.jmol.viewer.JmolConstants;
 
 public class AppJmol extends GStructureViewer implements StructureListener,
-        JmolStatusListener, Runnable
+        JmolStatusListener, Runnable, SequenceStructureBinding
 
 {
   JmolViewer viewer;
@@ -76,14 +78,15 @@ public class AppJmol extends GStructureViewer implements StructureListener,
   {
     this(file, id, seq, ap, loadStatus, bounds, null);
   }
+
   public AppJmol(String file, String id, SequenceI[] seq,
-            AlignmentPanel ap, String loadStatus, Rectangle bounds, String viewid)
+          AlignmentPanel ap, String loadStatus, Rectangle bounds,
+          String viewid)
   {
     loadingFromArchive = true;
     pdbentry = new PDBEntry();
     pdbentry.setFile(file);
     pdbentry.setId(id);
-    this.chains = chains;
     this.sequence = seq;
     this.ap = ap;
     this.setBounds(bounds);
@@ -225,15 +228,13 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     this.setTitle(title.toString());
     jalview.gui.Desktop.addInternalFrame(this, title.toString(),
             getBounds().width, getBounds().height);
+    // * OK, but safer to assign htmlName, URL bases, comandOptions, and
+    // statusListener now.
 
     viewer = org.jmol.api.JmolViewer.allocateViewer(renderPanel,
-            new SmarterJmolAdapter());
-
-    viewer.setAppletContext("", null, null, "");
+            new SmarterJmolAdapter(), "", null, null, "", this);
 
-    viewer.setJmolStatusListener(this);
-
-    jmolpopup = JmolPopup.newJmolPopup(viewer);
+    jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
 
     viewer.evalStringQuiet(command);
   }
@@ -280,14 +281,32 @@ public class AppJmol extends GStructureViewer implements StructureListener,
 
   void centerViewer()
   {
+    jmolHistory(false);
     StringBuffer cmd = new StringBuffer();
+    String lbl;
+    int mlength, p,mnum;
     for (int i = 0; i < chainMenu.getItemCount(); i++)
     {
       if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
       {
         JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
         if (item.isSelected())
-          cmd.append(":" + item.getText() + " or ");
+        {          lbl = item.getText();
+        mlength = 0;
+        do
+        {
+          p = mlength;
+          mlength = lbl.indexOf(":", p);
+        } while (p < mlength && mlength < (lbl.length() - 2));
+        if (pdbentry.getId().equals(lbl.substring(0,mlength)))
+        {
+          mnum = 1+getModelNum(pdbentry.getFile());
+        if (mnum>0)
+          {cmd.append(":" + lbl.substring(mlength + 1) + " /"
+                + mnum + " or ");
+          }
+        }
+        }
       }
     }
 
@@ -296,30 +315,60 @@ public class AppJmol extends GStructureViewer implements StructureListener,
 
     viewer.evalStringQuiet("select *;restrict " + cmd + ";cartoon;center "
             + cmd);
+    jmolHistory(true);
+  }
+  private int getModelNum(String modelFileName)
+  {
+    String[] mfn = getPdbFile();
+    if (mfn == null)
+    {
+      return -1;
+    }
+    for (int i = 0; i < mfn.length; i++)
+    {
+      if (mfn[i].equalsIgnoreCase(modelFileName))
+        return i;
+    }
+    return -1;
   }
 
   void closeViewer()
   {
     viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
+    // remove listeners for all structures in viewer
+    StructureSelectionManager.getStructureSelectionManager()
+            .removeStructureViewerListener(this, getPdbFile());
+    // and shut down jmol
     viewer.evalStringQuiet("zap");
     viewer.setJmolStatusListener(null);
     viewer = null;
-
-    // We'll need to find out what other
-    // listeners need to be shut down in Jmol
-    StructureSelectionManager.getStructureSelectionManager()
-            .removeStructureViewerListener(this, pdbentry.getFile());
   }
 
   public void run()
   {
     try
     {
-      EBIFetchClient ebi = new EBIFetchClient();
-      String query = "pdb:" + pdbentry.getId();
-      pdbentry.setFile(ebi.fetchDataAsFile(query, "default", "raw")
-              .getAbsolutePath());
-      initJmol("load " + pdbentry.getFile());
+      // TODO: replace with reference fetching/transfer code (validate PDBentry
+      // as a DBRef?)
+      jalview.ws.dbsources.Pdb pdbclient = new jalview.ws.dbsources.Pdb();
+      AlignmentI pdbseq;
+      if ((pdbseq = pdbclient.getSequenceRecords(pdbentry.getId())) != null)
+      {
+        // just transfer the file name from the first seuqence's first PDBEntry
+        pdbentry.setFile(((PDBEntry) pdbseq.getSequenceAt(0).getPDBId()
+                .elementAt(0)).getFile());
+        initJmol("load " + pdbentry.getFile());
+      }
+      else
+      {
+        JOptionPane
+                .showInternalMessageDialog(
+                        Desktop.desktop,
+                        pdbentry.getId()
+                                + " could not be retrieved. Please try downloading the file manually.",
+                        "Couldn't load file", JOptionPane.ERROR_MESSAGE);
+
+      }
     } catch (OutOfMemoryError oomerror)
     {
       new OOMWarning("Retrieving PDB id " + pdbentry.getId() + " from MSD",
@@ -379,7 +428,7 @@ public class AppJmol extends GStructureViewer implements StructureListener,
    * DOCUMENT ME!
    * 
    * @param e
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   public void eps_actionPerformed(ActionEvent e)
   {
@@ -390,7 +439,7 @@ public class AppJmol extends GStructureViewer implements StructureListener,
    * DOCUMENT ME!
    * 
    * @param e
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   public void png_actionPerformed(ActionEvent e)
   {
@@ -435,15 +484,19 @@ public class AppJmol extends GStructureViewer implements StructureListener,
   {
     colourBySequence = false;
     seqColour.setSelected(false);
+    jmolHistory(false);
     viewer.evalStringQuiet("select *;color chain");
+    jmolHistory(true);
   }
 
   public void chargeColour_actionPerformed(ActionEvent actionEvent)
   {
     colourBySequence = false;
     seqColour.setSelected(false);
+    jmolHistory(false);
     viewer.evalStringQuiet("select *;color white;select ASP,GLU;color red;"
             + "select LYS,ARG;color blue;select CYS;color yellow");
+    jmolHistory(true);
   }
 
   public void zappoColour_actionPerformed(ActionEvent actionEvent)
@@ -483,6 +536,7 @@ public class AppJmol extends GStructureViewer implements StructureListener,
 
   public void setJalviewColourScheme(ColourSchemeI cs)
   {
+    jmolHistory(false);
     colourBySequence = false;
     seqColour.setSelected(false);
 
@@ -509,6 +563,7 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     }
 
     viewer.evalStringQuiet(command.toString());
+    jmolHistory(true);
   }
 
   public void userColour_actionPerformed(ActionEvent actionEvent)
@@ -523,10 +578,16 @@ public class AppJmol extends GStructureViewer implements StructureListener,
 
     if (col != null)
     {
+      jmolHistory(false);
       viewer.evalStringQuiet("background [" + col.getRed() + ","
               + col.getGreen() + "," + col.getBlue() + "];");
+      jmolHistory(true);
     }
   }
+  private void jmolHistory(boolean enable)
+  {
+    viewer.setBooleanProperty("history", enable);
+  }
 
   public void jmolHelp_actionPerformed(ActionEvent actionEvent)
   {
@@ -538,39 +599,122 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     {
     }
   }
+  String[] modelFileNames = null;
 
   // ////////////////////////////////
   // /StructureListener
-  public String getPdbFile()
+  public String[] getPdbFile()
   {
-    return pdbentry.getFile();
+    if (modelFileNames == null)
+    {
+      String mset[] = new String[viewer.getModelCount()];
+      for (int i = 0; i < mset.length; i++)
+      {
+        try {
+          String mname = viewer.getModelFileName(i);
+          if (mname==null)
+          {
+            System.err.println("Model "+i+" has no filename!");
+            continue;
+          }
+          File fpath = new File(mname);
+          mset[i] = fpath.toString();
+        } catch (Exception e)
+        {
+          System.err.println("Couldn't parse "+viewer.getModelFileName(i)+" as a file!");
+        }
+      }
+      modelFileNames = mset;
+    }
+    return modelFileNames;
   }
 
   Pattern pattern = Pattern
-          .compile("\\[(.*)\\]([0-9]+)(:[a-zA-Z]*)?\\.([a-zA-Z]+)(/[0-9]*)?");
+          .compile("\\[(.*)\\]([0-9]+)(:[a-zA-Z]*)?\\.([a-zA-Z]+).*(/[0-9]*)?");
 
   String lastMessage;
 
   public void mouseOverStructure(int atomIndex, String strInfo)
   {
+    // copied from AppJmol - will be refactored to binding eventually
+    int pdbResNum;
+    int mdlSep = strInfo.indexOf("/");
+    int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
+
+    if (chainSeparator == -1)
+    {
+      chainSeparator = strInfo.indexOf(".");
+      if (mdlSep > -1 && mdlSep < chainSeparator)
+      {
+        chainSeparator1 = chainSeparator;
+        chainSeparator = mdlSep;
+      }
+    }
+    pdbResNum = Integer.parseInt(strInfo.substring(
+            strInfo.indexOf("]") + 1, chainSeparator));
+
+    String chainId;
+
+    if (strInfo.indexOf(":") > -1)
+      chainId = strInfo.substring(strInfo.indexOf(":") + 1, strInfo
+              .indexOf("."));
+    else
+    {
+      chainId = " ";
+    }
+
+    String pdbfilename = pdbentry.getFile();
+    if (mdlSep > -1)
+    {
+      if (chainSeparator1 == -1)
+      {
+        chainSeparator1 = strInfo.indexOf(".", mdlSep);
+      }
+      String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
+              chainSeparator1) : strInfo.substring(mdlSep + 1);
+      try
+      {
+        // recover PDB filename for the model hovered over.
+        pdbfilename = viewer
+                .getModelFileName(new Integer(mdlId).intValue() - 1);
+      } catch (Exception e)
+      {
+      }
+      ;
+    }
+    if (lastMessage == null || !lastMessage.equals(strInfo))
+      ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
+
+    lastMessage = strInfo;
+/*
+ * Old Implementation based on Pattern regex.
     Matcher matcher = pattern.matcher(strInfo);
     matcher.find();
     matcher.group(1);
     int pdbResNum = Integer.parseInt(matcher.group(2));
     String chainId = matcher.group(3);
-
+    
     if (chainId != null)
       chainId = chainId.substring(1, chainId.length());
     else
     {
       chainId = " ";
     }
+    String mdlId = matcher.group(4);
+    String pdbfilename = pdbentry.getFile();
 
+    if (mdlId!=null && mdlId.length()>0)
+    {
+      try {
+        // recover PDB filename for the model hovered over.
+        pdbfilename = viewer.getModelFileName(new Integer(mdlId).intValue()-1);
+      } catch (Exception e) {};
+    }
     if (lastMessage == null || !lastMessage.equals(strInfo))
     {
-      ssm.mouseOverStructure(pdbResNum, chainId, pdbentry.getFile());
+      ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
     }
-    lastMessage = strInfo;
+    lastMessage = strInfo; */
   }
 
   StringBuffer resetLastRes = new StringBuffer();
@@ -580,34 +724,47 @@ public class AppJmol extends GStructureViewer implements StructureListener,
   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
           String pdbfile)
   {
-    if (!pdbfile.equals(pdbentry.getFile()))
+    int mdlNum = 1+getModelNum(pdbfile);
+    if (mdlNum==0)
+    {
       return;
+    }
 
+    jmolHistory(false);
+    // if (!pdbfile.equals(pdbentry.getFile()))
+    // return;
     if (resetLastRes.length() > 0)
     {
       viewer.evalStringQuiet(resetLastRes.toString());
     }
 
     eval.setLength(0);
-    eval.append("select " + pdbResNum);
+    eval.append("select " + pdbResNum); // +modelNum
 
     resetLastRes.setLength(0);
-    resetLastRes.append("select " + pdbResNum);
+    resetLastRes.append("select " + pdbResNum); // +modelNum
 
     if (!chain.equals(" "))
     {
-      eval.append(":" + chain);
-      resetLastRes.append(":" + chain);
+      eval.append(":");
+      resetLastRes.append(":");
+      eval.append(chain);
+      resetLastRes.append(chain);
     }
-
-    eval.append(";wireframe 100;" + eval.toString() + ".CA;");
+    // if (mdlNum != 0)
+    {
+      eval.append(" /" + (mdlNum));
+      resetLastRes.append(" /" + (mdlNum));
+    }
+    eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
 
     resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
-            + ".CA;spacefill 0;");
+            + " and not hetero; spacefill 0;");
 
     eval.append("spacefill 200;select none");
 
     viewer.evalStringQuiet(eval.toString());
+    jmolHistory(true);
   }
 
   public Color getColour(int atomIndex, int pdbResNum, String chain,
@@ -638,11 +795,8 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     if (!colourBySequence || ap.alignFrame.getCurrentView() != ap.av)
       return;
 
-    StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
-
-    if (mapping.length < 1)
-      return;
-
+    String[] files = getPdbFile();
+    
     SequenceRenderer sr = new SequenceRenderer(ap.av);
 
     boolean showFeatures = false;
@@ -659,6 +813,13 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     }
 
     StringBuffer command = new StringBuffer();
+    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+    {
+      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
+
+      if (mapping == null || mapping.length < 1)
+        continue;
+
 
     int lastPos = -1;
     for (int sp, s = 0; s < sequence.length; s++)
@@ -687,36 +848,38 @@ public class AppJmol extends GStructureViewer implements StructureListener,
 
             if (showFeatures)
               col = fr.findFeatureColour(col, asp, r);
-
-            if (command.toString().endsWith(
-                    ":" + mapping[m].getChain() + ";color[" + col.getRed()
-                            + "," + col.getGreen() + "," + col.getBlue()
-                            + "]"))
+            String newSelcom = (mapping[m].getChain() != " " ? ":"
+                    + mapping[m].getChain() : "")
+                    + "/"
+                    + (pdbfnum + 1)
+                    + ".1"
+                    + ";color["
+                    + col.getRed()
+                    + ","
+                    + col.getGreen()
+                    + ","
+                    + col.getBlue() + "]";
+            if (command.toString().endsWith(newSelcom))
             {
               command = condenseCommand(command, pos);
               continue;
             }
 
             command.append(";select " + pos);
-
-            if (!mapping[m].getChain().equals(" "))
-            {
-              command.append(":" + mapping[m].getChain());
-            }
-
-            command.append(";color[" + col.getRed() + "," + col.getGreen()
-                    + "," + col.getBlue() + "]");
-
+            command.append(newSelcom);
           }
           break;
         }
       }
+      }
     }
+    jmolHistory(false);
 
     if (lastCommand == null || !lastCommand.equals(command.toString()))
     {
       viewer.evalStringQuiet(command.toString());
     }
+    jmolHistory(true);
     lastCommand = command.toString();
   }
 
@@ -758,13 +921,8 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     System.out.println("JMOL CREATE IMAGE");
   }
 
-  public void setCallbackFunction(String callbackType,
-          String callbackFunction)
-  {
-  }
-
-  public void notifyFileLoaded(String fullPathName, String fileName,
-          String modelName, Object clientFile, String errorMsg)
+  public void notifyFileLoaded(String fullPathName, String fileName2,
+          String modelName, String errorMsg, int modelParts)
   {
     if (errorMsg != null)
     {
@@ -774,29 +932,38 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     }
 
     fileLoadingError = null;
+    modelFileNames = null;
+    
+    String[] modelfilenames = getPdbFile();
+    ssm = StructureSelectionManager.getStructureSelectionManager();
+    boolean modelsloaded=false;
+    for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
+    {
+      String fileName = modelfilenames[modelnum];
 
     if (fileName != null)
     {
-
+      modelsloaded=true;
+      // search pdbentries and sequences to find correct pdbentry and sequence[] pair for this filename
+      if (pdbentry.getFile().equals(fileName))
+      {
+      // TODO: do some checking using the modelPts number of parts against our
+      // own estimate of the number of chains
       // FILE LOADED OK
-      ssm = StructureSelectionManager.getStructureSelectionManager();
       MCview.PDBfile pdbFile = ssm.setMapping(sequence, chains, pdbentry
               .getFile(), AppletFormatAdapter.FILE);
-      ssm.addStructureViewerListener(this);
       Vector chains = new Vector();
       for (int i = 0; i < pdbFile.chains.size(); i++)
       {
         chains
-                .addElement(((MCview.PDBChain) pdbFile.chains.elementAt(i)).id);
+                .addElement(new String(pdbFile.id+":"+((MCview.PDBChain) pdbFile.chains.elementAt(i)).id));
       }
       setChainMenuItems(chains);
 
-      jmolpopup.updateComputedMenus();
-
       if (!loadingFromArchive)
       {
         viewer
-                .evalStringQuiet("select backbone;restrict;cartoon;wireframe off;spacefill off");
+                .evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
 
         colourBySequence(ap);
       }
@@ -805,17 +972,23 @@ public class AppJmol extends GStructureViewer implements StructureListener,
 
       loadingFromArchive = false;
     }
-    else
-      return;
-  }
-
-  public void notifyFrameChanged(int frameNo)
-  {
-    boolean isAnimationRunning = (frameNo <= -2);
-  }
-
-  public void notifyScriptStart(String statusMessage, String additionalInfo)
-  {
+    else {
+      // this is a foreign pdb file that jalview doesn't know about - add it to the dataset
+      // and try to find a home - either on a matching sequence or as a new sequence.
+      String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
+              "PDB");
+      // parse pdb file into a chain, etc.
+      // locate best match for pdb in associated views and add mapping to
+      // ssm
+      modelsloaded=true;
+    }
+    }
+    }
+    if (modelsloaded)
+    {
+      ssm.addStructureViewerListener(this);
+      jmolpopup.updateComputedMenus();
+    }
   }
 
   public void sendConsoleEcho(String strEcho)
@@ -843,15 +1016,17 @@ public class AppJmol extends GStructureViewer implements StructureListener,
 
   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
   {
-    notifyAtomPicked(iatom, strMeasure);
+    notifyAtomPicked(iatom, strMeasure, null);
   }
 
-  public void notifyNewDefaultModeMeasurement(int count, String strInfo)
-  {
-  }
-
-  public void notifyAtomPicked(int atomIndex, String strInfo)
+  public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
   {
+    if (strData != null)
+    {
+      Cache.log.info("Non null pick data string: " + strData
+              + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
+    }
+    /*
     Matcher matcher = pattern.matcher(strInfo);
     matcher.find();
 
@@ -861,17 +1036,35 @@ public class AppJmol extends GStructureViewer implements StructureListener,
 
     String picked = resnum;
 
+
     if (chainId != null)
       picked += (":" + chainId.substring(1, chainId.length()));
-
-    picked += ".CA";
-
+*/
+    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)
+            {
+      mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
+            }
+    picked = "((" + picked + ".CA" + mdlString+")|(" + picked + ".P" + mdlString+"))";
+    jmolHistory(false);
     if (!atomsPicked.contains(picked))
     {
-      if (chainId != null)
+      // TODO: re-instate chain ID separator dependent labelling for both applet and application
+//      if (chainId != null)
         viewer.evalString("select " + picked + ";label %n %r:%c");
-      else
-        viewer.evalString("select " + picked + ";label %n %r");
+//      else
+//        viewer.evalString("select " + picked + ";label %n %r");
       atomsPicked.addElement(picked);
     }
     else
@@ -879,7 +1072,7 @@ public class AppJmol extends GStructureViewer implements StructureListener,
       viewer.evalString("select " + picked + ";label off");
       atomsPicked.removeElement(picked);
     }
-
+    jmolHistory(true);
     if (scriptWindow != null)
     {
       scriptWindow.sendConsoleMessage(strInfo);
@@ -887,17 +1080,27 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     }
   }
 
-  public void notifyAtomHovered(int atomIndex, String strInfo)
+  public void notifyAtomHovered(int atomIndex, String strInfo, String data)
   {
+    if (data != null)
+    {
+      Cache.log.info("Non null hover data string: " + data
+              + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
+    }
     mouseOverStructure(atomIndex, strInfo);
   }
 
-  public void sendSyncScript(String script, String appletName)
-  {
-  }
-
+  @Override
   public void showUrl(String url)
   {
+    try
+    {
+      jalview.util.BrowserLauncher.openURL(url);
+    } catch (IOException e)
+    {
+      Cache.log.error("Failed to launch Jmol-associated url " + url, e);
+      // TODO: 2.6 : warn user if browser was not configured.
+    }
   }
 
   public void showConsole(boolean showConsole)
@@ -931,9 +1134,9 @@ public class AppJmol extends GStructureViewer implements StructureListener,
     validate();
   }
 
-  public float functionXY(String functionName, int x, int y)
+  public float[][] functionXY(String functionName, int x, int y)
   {
-    return 0;
+    return null;
   }
 
   // /End JmolStatusListener
@@ -973,14 +1176,114 @@ public class AppJmol extends GStructureViewer implements StructureListener,
       }
     }
   }
-  String viewId=null;
+
+  String viewId = null;
+
   public String getViewId()
   {
-    if (viewId==null)
+    if (viewId == null)
     {
-      viewId=System.currentTimeMillis()+"."+this.hashCode();
+      viewId = System.currentTimeMillis() + "." + this.hashCode();
     }
     return viewId;
   }
 
+  @Override
+  public String createImage(String fileName, String type,
+          Object textOrBytes, int quality)
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  @Override
+  public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  @Override
+  public Hashtable getRegistryInfo()
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  @Override
+  public void notifyCallback(int type, Object[] data)
+  {
+    try
+    {
+      switch (type)
+      {
+      case JmolConstants.CALLBACK_LOADSTRUCT:
+        notifyFileLoaded((String) data[1], (String) data[2],
+                (String) data[3], (String) data[4], ((Integer) data[5])
+                        .intValue());
+
+        break;
+      case JmolConstants.CALLBACK_PICK:
+        notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
+                (String) data[0]);
+        // also highlight in alignment
+      case JmolConstants.CALLBACK_HOVER:
+        notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
+                (String) data[0]);
+        break;
+      case JmolConstants.CALLBACK_SCRIPT:
+        notifyScriptTermination((String) data[2], ((Integer) data[3])
+                .intValue());
+        break;
+      case JmolConstants.CALLBACK_ECHO:
+        sendConsoleEcho((String) data[1]);
+        break;
+      case JmolConstants.CALLBACK_MESSAGE:
+        sendConsoleMessage((data == null) ? ((String) null)
+                : (String) data[1]);
+        break;
+      case JmolConstants.CALLBACK_MEASURE:
+      case JmolConstants.CALLBACK_CLICK:
+      default:
+        System.err.println("Unhandled callback " + type + " " + data);
+        break;
+      }
+    } catch (Exception e)
+    {
+      Cache.log.warn("Squashed Jmol callback handler error: ", e);
+    }
+  }
+
+  @Override
+  public boolean notifyEnabled(int callbackPick)
+  {
+    switch (callbackPick)
+    {
+    case JmolConstants.CALLBACK_ECHO:
+    case JmolConstants.CALLBACK_LOADSTRUCT:
+    case JmolConstants.CALLBACK_MEASURE:
+    case JmolConstants.CALLBACK_MESSAGE:
+    case JmolConstants.CALLBACK_PICK:
+    case JmolConstants.CALLBACK_SCRIPT:
+    case JmolConstants.CALLBACK_HOVER:
+    case JmolConstants.CALLBACK_ERROR:
+      return true;
+    case JmolConstants.CALLBACK_CLICK:
+    case JmolConstants.CALLBACK_ANIMFRAME:
+    case JmolConstants.CALLBACK_MINIMIZATION:
+    case JmolConstants.CALLBACK_RESIZE:
+    case JmolConstants.CALLBACK_SYNC:
+    }
+    return false;
+  }
+
+  @Override
+  public void setCallbackFunction(String callbackType,
+          String callbackFunction)
+  {
+    Cache.log.debug("Ignoring set-callback request to associate "
+            + callbackType + " with function " + callbackFunction);
+
+  }
+
 }