JAL-3626 use Viewer.scriptWait() to send Jmol commands (Bob Hanson)
[jalview.git] / src / jalview / gui / AppJmol.java
index fef7451..ffc8053 100644 (file)
  */
 package jalview.gui;
 
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.structures.models.AAStructureBindingModel;
-import jalview.util.BrowserLauncher;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.ws.dbsources.Pdb;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Graphics;
 import java.awt.Rectangle;
-import java.awt.event.ActionEvent;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
-import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JInternalFrame;
 import javax.swing.JPanel;
 import javax.swing.JSplitPane;
 import javax.swing.SwingUtilities;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.ImageExporter.ImageWriterI;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.structure.StructureCommand;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.BrowserLauncher;
+import jalview.util.ImageMaker;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.dbsources.Pdb;
+
 public class AppJmol extends StructureViewerBase
 {
   // ms to wait for Jmol to load files
@@ -58,7 +58,7 @@ public class AppJmol extends StructureViewerBase
 
   private static final String SPACE = " ";
 
-  private static final String BACKSLASH = "\"";
+  private static final String QUOTE = "\"";
 
   AppJmolBinding jmb;
 
@@ -155,15 +155,8 @@ public class AppJmol extends StructureViewerBase
             .getString("label.let_jmol_manage_structure_colours"));
   }
 
-  IProgressIndicator progressBar = null;
-
-  @Override
-  protected IProgressIndicator getIProgressIndicator()
-  {
-    return progressBar;
-  }
   /**
-   * add a single PDB structure to a new or existing Jmol view
+   * display a single PDB structure in a new Jmol view
    * 
    * @param pdbentry
    * @param seq
@@ -173,47 +166,29 @@ public class AppJmol extends StructureViewerBase
   public AppJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
           final AlignmentPanel ap)
   {
-    progressBar = ap.alignFrame;
-    String pdbId = pdbentry.getId();
-
-    /*
-     * If the PDB file is already loaded, the user may just choose to add to an
-     * existing viewer (or cancel)
-     */
-    if (addAlreadyLoadedFile(seq, chains, ap, pdbId))
-    {
-      return;
-    }
+    setProgressIndicator(ap.alignFrame);
 
-    /*
-     * Check if there are other Jmol views involving this alignment and prompt
-     * user about adding this molecule to one of them
-     */
-    if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId))
-    {
-      return;
-    }
-
-    /*
-     * If the options above are declined or do not apply, open a new viewer
-     */
-    openNewJmol(ap, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq });
+    openNewJmol(ap, alignAddedStructures, new PDBEntry[] { pdbentry },
+            new SequenceI[][]
+            { seq });
   }
 
-  private void openNewJmol(AlignmentPanel ap, PDBEntry[] pdbentrys,
+  private void openNewJmol(AlignmentPanel ap, boolean alignAdded,
+          PDBEntry[] pdbentrys,
           SequenceI[][] seqs)
   {
-    progressBar = ap.alignFrame;
+    setProgressIndicator(ap.alignFrame);
     jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
             pdbentrys, seqs, null);
     addAlignmentPanel(ap);
     useAlignmentPanelForColourbyseq(ap);
 
+    alignAddedStructures = alignAdded;
     if (pdbentrys.length > 1)
     {
-      alignAddedStructures = true;
       useAlignmentPanelForSuperposition(ap);
     }
+
     jmb.setColourBySequence(true);
     setSize(400, 400); // probably should be a configurable/dynamic default here
     initMenus();
@@ -234,41 +209,21 @@ public class AppJmol extends StructureViewerBase
   }
 
   /**
-   * create a new Jmol containing several structures superimposed using the
-   * given alignPanel.
+   * create a new Jmol containing several structures optionally superimposed
+   * using the given alignPanel.
    * 
    * @param ap
+   * @param alignAdded
+   *          - true to superimpose
    * @param pe
    * @param seqs
    */
-  public AppJmol(AlignmentPanel ap, PDBEntry[] pe, SequenceI[][] seqs)
+  public AppJmol(AlignmentPanel ap, boolean alignAdded, PDBEntry[] pe,
+          SequenceI[][] seqs)
   {
-    openNewJmol(ap, pe, seqs);
+    openNewJmol(ap, alignAdded, pe, seqs);
   }
 
-  /**
-   * Returns a list of any Jmol viewers. The list is restricted to those linked
-   * to the given alignment panel if it is not null.
-   */
-  @Override
-  protected List<StructureViewerBase> getViewersFor(AlignmentPanel apanel)
-  {
-    List<StructureViewerBase> result = new ArrayList<>();
-    JInternalFrame[] frames = Desktop.instance.getAllFrames();
-
-    for (JInternalFrame frame : frames)
-    {
-      if (frame instanceof AppJmol)
-      {
-        if (apanel == null
-                || ((StructureViewerBase) frame).isLinkedWith(apanel))
-        {
-          result.add((StructureViewerBase) frame);
-        }
-      }
-    }
-    return result;
-  }
 
   void initJmol(String command)
   {
@@ -295,48 +250,11 @@ public class AppJmol extends StructureViewerBase
     {
       command = "";
     }
-    jmb.evalStateCommand(command);
-    jmb.evalStateCommand("set hoverDelay=0.1");
+    jmb.executeCommand(new StructureCommand(command), false);
+    jmb.executeCommand(new StructureCommand("set hoverDelay=0.1"), false);
     jmb.setFinishedInit(true);
   }
 
-  boolean allChainsSelected = false;
-
-  @Override
-  void showSelectedChains()
-  {
-    Vector<String> toshow = new Vector<>();
-    for (int i = 0; i < chainMenu.getItemCount(); i++)
-    {
-      if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
-      {
-        JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
-        if (item.isSelected())
-        {
-          toshow.addElement(item.getText());
-        }
-      }
-    }
-    jmb.centerViewer(toshow);
-  }
-
-  @Override
-  public void closeViewer(boolean closeExternalViewer)
-  {
-    // Jmol does not use an external viewer
-    if (jmb != null)
-    {
-      jmb.closeViewer();
-    }
-    setAlignmentPanel(null);
-    _aps.clear();
-    _alignwith.clear();
-    _colourwith.clear();
-    // TODO: check for memory leaks where instance isn't finalised because jmb
-    // holds a reference to the window
-    jmb = null;
-  }
-
   @Override
   public void run()
   {
@@ -368,8 +286,8 @@ public class AppJmol extends StructureViewerBase
     StringBuilder fileList = new StringBuilder();
     for (String s : files)
     {
-      fileList.append(SPACE).append(BACKSLASH)
-              .append(Platform.escapeString(s)).append(BACKSLASH);
+      fileList.append(SPACE).append(QUOTE)
+              .append(Platform.escapeBackslashes(s)).append(QUOTE);
     }
     String filesString = fileList.toString();
 
@@ -385,6 +303,8 @@ public class AppJmol extends StructureViewerBase
       } catch (Exception ex)
       {
         Cache.log.error("Couldn't open Jmol viewer!", ex);
+        ex.printStackTrace();
+        return;
       }
     }
     else
@@ -393,20 +313,23 @@ public class AppJmol extends StructureViewerBase
       cmd.append("loadingJalviewdata=true\nload APPEND ");
       cmd.append(filesString);
       cmd.append("\nloadingJalviewdata=null");
-      final String command = cmd.toString();
+      final StructureCommand command = new StructureCommand(cmd.toString());
       lastnotify = jmb.getLoadNotifiesHandled();
 
       try
       {
-        jmb.evalStateCommand(command);
+        jmb.executeCommand(command, false);
       } catch (OutOfMemoryError oomerror)
       {
         new OOMWarning("When trying to add structures to the Jmol viewer!",
                 oomerror);
         Cache.log.debug("File locations are " + filesString);
+        return;
       } catch (Exception ex)
       {
         Cache.log.error("Couldn't add files to Jmol viewer!", ex);
+        ex.printStackTrace();
+        return;
       }
     }
 
@@ -421,8 +344,10 @@ public class AppJmol extends StructureViewerBase
       try
       {
         Cache.log.debug("Waiting around for jmb notify.");
-        Thread.sleep(waitFor);
         waitTotal += waitFor;
+
+        // Thread.sleep() throws an exception in JS
+        Thread.sleep(waitFor);
       } catch (Exception e)
       {
       }
@@ -439,12 +364,12 @@ public class AppJmol extends StructureViewerBase
     }
 
     // refresh the sequence colours for the new structure(s)
-    for (AlignmentPanel ap : _colourwith)
+    for (AlignmentViewPanel ap : _colourwith)
     {
       jmb.updateColours(ap);
     }
     // do superposition if asked to
-    if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures)
+    if (alignAddedStructures)
     {
       alignAddedStructures();
     }
@@ -461,7 +386,7 @@ public class AppJmol extends StructureViewerBase
       @Override
       public void run()
       {
-        if (jmb.viewer.isScriptExecuting())
+        if (jmb.jmolViewer.isScriptExecuting())
         {
           SwingUtilities.invokeLater(this);
           try
@@ -474,11 +399,11 @@ public class AppJmol extends StructureViewerBase
         }
         else
         {
-          alignStructs_withAllAlignPanels();
+          alignStructsWithAllAlignPanels();
         }
       }
     });
-    alignAddedStructures = false;
+
   }
 
   /**
@@ -507,16 +432,14 @@ public class AppJmol extends StructureViewerBase
         String file = jmb.getPdbEntry(pi).getFile();
         if (file == null)
         {
+          // todo: extract block as method and pull up (also ChimeraViewFrame)
           // retrieve the pdb and store it locally
           AlignmentI pdbseq = null;
           pdbid = jmb.getPdbEntry(pi).getId();
           long hdl = pdbid.hashCode() - System.currentTimeMillis();
-          if (progressBar != null)
-          {
-            progressBar.setProgressBar(MessageManager
-                    .formatMessage("status.fetching_pdb", new String[]
-                    { pdbid }), hdl);
-          }
+          setProgressMessage(MessageManager
+                  .formatMessage("status.fetching_pdb", new String[]
+                  { pdbid }), hdl);
           try
           {
             pdbseq = pdbclient.getSequenceRecords(pdbid);
@@ -529,12 +452,8 @@ public class AppJmol extends StructureViewerBase
             errormsgs.append("'").append(pdbid).append("'");
           } finally
           {
-            if (progressBar != null)
-            {
-              progressBar.setProgressBar(
-                      MessageManager.getString("label.state_completed"),
-                      hdl);
-            }
+            setProgressMessage(
+                    MessageManager.getString("label.state_completed"), hdl);
           }
           if (pdbseq != null)
           {
@@ -557,7 +476,7 @@ public class AppJmol extends StructureViewerBase
             addingStructures = true; // already files loaded.
             for (int c = 0; c < filesInViewer.length; c++)
             {
-              if (filesInViewer[c].equals(file))
+              if (Platform.pathEquals(filesInViewer[c], file))
               {
                 file = null;
                 break;
@@ -591,67 +510,48 @@ public class AppJmol extends StructureViewerBase
     return files;
   }
 
+  /**
+   * Outputs the Jmol viewer image as an image file, after prompting the user to
+   * choose a file and (for EPS) choice of Text or Lineart character rendering
+   * (unless a preference for this is set)
+   * 
+   * @param type
+   */
   @Override
-  public void eps_actionPerformed(ActionEvent e)
-  {
-    makePDBImage(jalview.util.ImageMaker.TYPE.EPS);
-  }
-
-  @Override
-  public void png_actionPerformed(ActionEvent e)
-  {
-    makePDBImage(jalview.util.ImageMaker.TYPE.PNG);
-  }
-
-  void makePDBImage(jalview.util.ImageMaker.TYPE type)
+  public void makePDBImage(ImageMaker.TYPE type)
   {
     int width = getWidth();
     int height = getHeight();
-
-    jalview.util.ImageMaker im;
-
-    if (type == jalview.util.ImageMaker.TYPE.PNG)
+    ImageWriterI writer = new ImageWriterI()
     {
-      im = new jalview.util.ImageMaker(this,
-              jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from view",
-              width, height, null, null, null, 0, false);
-    }
-    else if (type == jalview.util.ImageMaker.TYPE.EPS)
-    {
-      im = new jalview.util.ImageMaker(this,
-              jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from view",
-              width, height, null, this.getTitle(), null, 0, false);
-    }
-    else
-    {
-
-      im = new jalview.util.ImageMaker(this,
-              jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
-              width, height, null, this.getTitle(), null, 0, false);
-    }
-
-    if (im.getGraphics() != null)
-    {
-      jmb.viewer.renderScreenImage(im.getGraphics(), width, height);
-      im.writeImage();
-    }
+      @Override
+      public void exportImage(Graphics g) throws Exception
+      {
+        jmb.jmolViewer.renderScreenImage(g, width, height);
+      }
+    };
+    String view = MessageManager.getString("action.view").toLowerCase();
+    ImageExporter exporter = new ImageExporter(writer,
+            getProgressIndicator(), type, getTitle());
+    exporter.doExport(null, this, width, height, view);
   }
 
   @Override
-  public void showHelp_actionPerformed(ActionEvent actionEvent)
+  public void showHelp_actionPerformed()
   {
     try
     {
-      BrowserLauncher
-              .openURL("http://jmol.sourceforge.net/docs/JmolUserGuide/");
+      BrowserLauncher // BH 2018
+              .openURL("http://wiki.jmol.org");//http://jmol.sourceforge.net/docs/JmolUserGuide/");
     } catch (Exception ex)
     {
+      System.err.println("Show Jmol help failed with: " + ex.getMessage());
     }
   }
 
+  @Override
   public void showConsole(boolean showConsole)
   {
-
     if (showConsole)
     {
       if (splitPane == null)
@@ -717,7 +617,7 @@ public class AppJmol extends StructureViewerBase
           }
         }
       }
-      else if (jmb == null || jmb.viewer == null || !jmb.isFinishedInit())
+      else if (jmb == null || jmb.jmolViewer == null || !jmb.isFinishedInit())
       {
         g.setColor(Color.black);
         g.fillRect(0, 0, currentSize.width, currentSize.height);
@@ -728,7 +628,7 @@ public class AppJmol extends StructureViewerBase
       }
       else
       {
-        jmb.viewer.renderScreenImage(g, currentSize.width,
+        jmb.jmolViewer.renderScreenImage(g, currentSize.width,
                 currentSize.height);
       }
     }
@@ -741,12 +641,6 @@ public class AppJmol extends StructureViewerBase
   }
 
   @Override
-  public String getStateInfo()
-  {
-    return jmb == null ? null : jmb.viewer.getStateInfo();
-  }
-
-  @Override
   public ViewerType getViewerType()
   {
     return ViewerType.JMOL;