JAL-4125 Additions to JvOptionPane and QuitHandler with StructureViewerBase handling...
[jalview.git] / src / jalview / gui / StructureViewerBase.java
index 33a122c..9a575ff 100644 (file)
@@ -26,6 +26,7 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
+import java.beans.PropertyVetoException;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -36,6 +37,9 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 import java.util.Vector;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import javax.swing.ButtonGroup;
 import javax.swing.JCheckBoxMenuItem;
@@ -47,6 +51,7 @@ import javax.swing.event.MenuListener;
 
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
@@ -63,7 +68,9 @@ import jalview.structure.StructureMapping;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.BrowserLauncher;
 import jalview.util.MessageManager;
+import jalview.ws.dbsources.EBIAlfaFold;
 import jalview.ws.dbsources.Pdb;
+import jalview.ws.utils.UrlDownloadClient;
 
 /**
  * Base class with common functionality for JMol, Chimera or other structure
@@ -132,6 +139,7 @@ public abstract class StructureViewerBase extends GStructureViewer
   public StructureViewerBase()
   {
     super();
+    setFrameIcon(null);
   }
 
   /**
@@ -155,6 +163,17 @@ public abstract class StructureViewerBase extends GStructureViewer
   }
 
   /**
+   * called by the binding model to indicate when adding structures is happening
+   * or has been completed
+   * 
+   * @param addingStructures
+   */
+  public synchronized void setAddingStructures(boolean addingStructures)
+  {
+    this.addingStructures = addingStructures;
+  }
+
+  /**
    * 
    * @param ap2
    * @return true if this Jmol instance is linked with the given alignPanel
@@ -210,6 +229,10 @@ public abstract class StructureViewerBase extends GStructureViewer
       _alignwith.add(ap);
     }
     ;
+    // TODO: refactor to allow concrete classes to register buttons for adding
+    // here
+    // currently have to override to add buttons back in after they are cleared
+    // in this loop
     for (Component c : viewerActionMenu.getMenuComponents())
     {
       if (c != alignStructs)
@@ -427,8 +450,9 @@ public abstract class StructureViewerBase extends GStructureViewer
     {
       return;
     }
-    AlignmentPanel alignPanel = (AlignmentPanel) apanel; // Implementation error if this
-                                                 // cast fails
+    AlignmentPanel alignPanel = (AlignmentPanel) apanel; // Implementation error
+                                                         // if this
+    // cast fails
     useAlignmentPanelForSuperposition(alignPanel);
     addStructure(pdbentry, seq, chains, alignPanel.alignFrame);
   }
@@ -565,9 +589,8 @@ public abstract class StructureViewerBase extends GStructureViewer
   public void changeColour_actionPerformed(String colourSchemeName)
   {
     AlignmentI al = getAlignmentPanel().av.getAlignment();
-    ColourSchemeI cs = ColourSchemes.getInstance()
-            .getColourScheme(colourSchemeName, getAlignmentPanel().av, al,
-                    null);
+    ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(
+            colourSchemeName, getAlignmentPanel().av, al, null);
     getBinding().colourByJalviewColourScheme(cs);
   }
 
@@ -799,7 +822,7 @@ public abstract class StructureViewerBase extends GStructureViewer
       {
         sp.append("'" + alignPanel.getViewName() + "' ");
       }
-      Cache.log.info("Couldn't align structures with the " + sp.toString()
+      Console.info("Couldn't align structures with the " + sp.toString()
               + "associated alignment panels.", e);
     }
     return reply;
@@ -1022,14 +1045,17 @@ public abstract class StructureViewerBase extends GStructureViewer
     {
       return false;
     }
-    int p=0;
-    for (String pdbid:pdbids) {
+    int p = 0;
+    for (String pdbid : pdbids)
+    {
       StructureMapping sm[] = getBinding().getSsm().getMapping(pdbid);
-      if (sm!=null && sm.length>0 && sm[0]!=null) {
+      if (sm != null && sm.length > 0 && sm[0] != null)
+      {
         p++;
       }
     }
-    // only return true if there is a mapping for every structure file we have loaded
+    // only return true if there is a mapping for every structure file we have
+    // loaded
     if (p == 0 || p != pdbids.length)
     {
       return false;
@@ -1076,7 +1102,7 @@ public abstract class StructureViewerBase extends GStructureViewer
     progressBar = pi;
   }
 
-  protected void setProgressMessage(String message, long id)
+  public void setProgressMessage(String message, long id)
   {
     if (progressBar != null)
     {
@@ -1121,11 +1147,12 @@ public abstract class StructureViewerBase extends GStructureViewer
   {
     String filePath = null;
     Pdb pdbclient = new Pdb();
+    EBIAlfaFold afclient = new EBIAlfaFold();
     AlignmentI pdbseq = null;
     String pdbid = processingEntry.getId();
     long handle = System.currentTimeMillis()
             + Thread.currentThread().hashCode();
-  
+
     /*
      * Write 'fetching PDB' progress on AlignFrame as we are not yet visible
      */
@@ -1138,7 +1165,39 @@ public abstract class StructureViewerBase extends GStructureViewer
     // { pdbid }));
     try
     {
-      pdbseq = pdbclient.getSequenceRecords(pdbid);
+      if (afclient.isValidReference(pdbid))
+      {
+        pdbseq = afclient.getSequenceRecords(pdbid,
+                processingEntry.getRetrievalUrl());
+      }
+      else
+      {
+        if (processingEntry.hasRetrievalUrl())
+        {
+          String safePDBId = java.net.URLEncoder.encode(pdbid, "UTF-8")
+                  .replace("%", "__");
+
+          // retrieve from URL to new local tmpfile
+          File tmpFile = File.createTempFile(safePDBId,
+                  "." + (PDBEntry.Type.MMCIF.toString().equals(
+                          processingEntry.getType().toString()) ? "cif"
+                                  : "pdb"));
+          String fromUrl = processingEntry.getRetrievalUrl();
+          UrlDownloadClient.download(fromUrl, tmpFile);
+
+          // may not need this check ?
+          String file = tmpFile.getAbsolutePath();
+          if (file != null)
+          {
+            pdbseq = EBIAlfaFold.importDownloadedStructureFromUrl(fromUrl,
+                    tmpFile, pdbid, null, null, null);
+          }
+        }
+        else
+        {
+          pdbseq = pdbclient.getSequenceRecords(pdbid);
+        }
+      }
     } catch (Exception e)
     {
       System.err.println(
@@ -1171,8 +1230,29 @@ public abstract class StructureViewerBase extends GStructureViewer
    */
   public File saveSession()
   {
-    // TODO: a wait loop to ensure the file is written fully before returning?
-    return getBinding() == null ? null : getBinding().saveSession();
+    if (getBinding() == null)
+    {
+      return null;
+    }
+    File session = getBinding().saveSession();
+    long l = session.length();
+    int wait = 50;
+    do
+    {
+      try
+      {
+        Thread.sleep(5);
+      } catch (InterruptedException e)
+      {
+      }
+      long nextl = session.length();
+      if (nextl != l)
+      {
+        wait = 50;
+        l = nextl;
+      }
+    } while (--wait > 0);
+    return session;
   }
 
   /**
@@ -1192,23 +1272,40 @@ public abstract class StructureViewerBase extends GStructureViewer
       if (!forceClose)
       {
         String viewerName = getViewerName();
+
+        int confirm = JvOptionPane.CANCEL_OPTION;
         String prompt = MessageManager
                 .formatMessage("label.confirm_close_viewer", new Object[]
                 { binding.getViewerTitle(viewerName, false), viewerName });
         prompt = JvSwingUtils.wrapTooltip(true, prompt);
-        int confirm = JvOptionPane.showConfirmDialog(this, prompt,
-                MessageManager.getString("label.close_viewer"),
-                JvOptionPane.YES_NO_CANCEL_OPTION);
+        String title = MessageManager.getString("label.close_viewer");
+        confirm = showCloseDialog(title, prompt);
+
         /*
          * abort closure if user hits escape or Cancel
          */
         if (confirm == JvOptionPane.CANCEL_OPTION
                 || confirm == JvOptionPane.CLOSED_OPTION)
         {
+          // abort possible quit handling if CANCEL chosen
+          if (confirm == JvOptionPane.CANCEL_OPTION)
+          {
+            try
+            {
+              // this is a bit futile
+              this.setClosed(false);
+            } catch (PropertyVetoException e)
+            {
+            }
+            QuitHandler.abortQuit();
+          }
           return;
         }
         forceClose = confirm == JvOptionPane.YES_OPTION;
       }
+    }
+    if (binding != null)
+    {
       binding.closeViewer(forceClose);
     }
     setAlignmentPanel(null);
@@ -1221,22 +1318,89 @@ public abstract class StructureViewerBase extends GStructureViewer
     dispose();
   }
 
+  private int showCloseDialog(final String title, final String prompt)
+  {
+    confirmResponse = JvOptionPane.CANCEL_OPTION;
+
+    if (QuitHandler.quitting())
+    {
+
+      Callable<Void> yesCall = () -> {
+        Console.debug("YES");
+        confirmResponse = JvOptionPane.YES_OPTION;
+        return null;
+      };
+      Callable<Void> noCall = () -> {
+        Console.debug("NO");
+        confirmResponse = JvOptionPane.NO_OPTION;
+        return null;
+      };
+      Callable<Void> cancelCall = () -> {
+        Console.debug("CANCEL");
+        confirmResponse = JvOptionPane.CANCEL_OPTION;
+        return null;
+      };
+      Callable<Void>[] calls = new Callable[] { yesCall, noCall,
+          cancelCall };
+      String cancelQuit = MessageManager.getString("action.cancel_quit");
+      String[] buttonsText = { MessageManager.getString("action.yes"),
+          MessageManager.getString("action.no"), cancelQuit };
+      JvOptionPane dialog = JvOptionPane.frameDialog(prompt,
+              MessageManager.getString("label.close_viewer"),
+              JvOptionPane.WARNING_MESSAGE, buttonsText, cancelQuit, calls,
+              false);
+      // wait for response
+      ExecutorService executor = dialog.getExecutor();
+      executor.shutdown();
+      try
+      {
+        Console.debug("### executor.awaitTermination() starting");
+        executor.awaitTermination(60, TimeUnit.SECONDS);
+        Console.debug("### executor.awaitTermination() finished");
+      } catch (InterruptedException e)
+      {
+      }
+    }
+    else
+    {
+      confirmResponse = JvOptionPane.showConfirmDialog(this, prompt,
+              MessageManager.getString("label.close_viewer"),
+              JvOptionPane.YES_NO_CANCEL_OPTION,
+              JvOptionPane.WARNING_MESSAGE);
+    }
+    return confirmResponse;
+  }
+
   @Override
   public void showHelp_actionPerformed()
   {
+    /*
     try
     {
-      String url = getBinding().getHelpURL();
-      if (url != null)
-      {
-        BrowserLauncher.openURL(url);
-      }
-    } catch (IOException ex)
+    */
+    String url = getBinding().getHelpURL();
+    if (url != null)
+    {
+      BrowserLauncher.openURL(url);
+    }
+    /* 
+    }
+    catch (IOException ex)
     {
       System.err
               .println("Show " + getViewerName() + " failed with: "
                       + ex.getMessage());
     }
+    */
+  }
+
+  @Override
+  public boolean hasViewerActionsMenu()
+  {
+    return viewerActionMenu != null && viewerActionMenu.isEnabled()
+            && viewerActionMenu.getItemCount() > 0
+            && viewerActionMenu.isVisible();
   }
 
+  private static int confirmResponse = 0;
 }