Merge branch 'develop' into bug/JAL-4059_update_swingJS_for_JalviewJS_2_11_2_and_2_11_3
[jalview.git] / src / jalview / bin / Commands.java
index 592ac85..e352fd8 100644 (file)
@@ -3,7 +3,6 @@ package jalview.bin;
 import java.awt.Color;
 import java.io.File;
 import java.io.IOException;
-import java.lang.reflect.Field;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -14,6 +13,8 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
+import javax.swing.SwingUtilities;
+
 import jalview.analysis.AlignmentUtils;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Jalview.ExitCode;
@@ -25,8 +26,6 @@ import jalview.bin.argparser.SubVals;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.annotations.AlphaFoldAnnotationRowBuilder;
-import jalview.ext.jmol.JalviewJmolBinding;
-import jalview.ext.jmol.JmolCommands;
 import jalview.gui.AlignFrame;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.AppJmol;
@@ -51,11 +50,13 @@ import jalview.io.exceptions.ImageOutputException;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.structure.StructureCommandI;
-import jalview.structure.StructureCommandsI;
 import jalview.structure.StructureImportSettings.TFType;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.ColorUtils;
 import jalview.util.FileUtils;
 import jalview.util.HttpUtils;
+import jalview.util.IdUtils;
+import jalview.util.IdUtils.IdType;
 import jalview.util.ImageMaker;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
@@ -148,10 +149,14 @@ public class Commands
 
     }
 
-    // report errors
-    Console.warn(
-            "The following errors and warnings occurred whilst processing files:\n"
-                    + errorsToString());
+    // report errors - if any
+    String errorsRaised = errorsToString();
+    if (errorsRaised.trim().length() > 0)
+    {
+      Console.warn(
+              "The following errors and warnings occurred whilst processing files:\n"
+                      + errorsRaised);
+    }
     // gui errors reported in Jalview
 
     if (argParser.getBoolean(Arg.QUIT))
@@ -193,7 +198,7 @@ public class Commands
     if (avm.containsArg(Arg.APPEND) || avm.containsArg(Arg.OPEN))
     {
       commandArgsProvided = true;
-      long progress = -1;
+      final long progress = IdUtils.newId(IdType.PROGRESS);
 
       boolean first = true;
       boolean progressBarSet = false;
@@ -219,10 +224,18 @@ public class Commands
           first = false;
           if (!headless && desktop != null)
           {
-            desktop.setProgressBar(
-                    MessageManager.getString(
-                            "status.processing_commandline_args"),
-                    progress = System.currentTimeMillis());
+            SwingUtilities.invokeLater(new Runnable()
+            {
+              @Override
+              public void run()
+              {
+                desktop.setProgressBar(
+                        MessageManager.getString(
+                                "status.processing_commandline_args"),
+                        progress);
+
+              }
+            });
             progressBarSet = true;
           }
         }
@@ -364,24 +377,49 @@ public class Commands
           boolean showSSAnnotations = avm.getFromSubValArgOrPref(
                   Arg.SHOWSSANNOTATIONS, av.getSubVals(), null,
                   "STRUCT_FROM_PDB", true);
-          af.setAnnotationsVisibility(showSSAnnotations, true, false);
 
           // Show sequence annotations?
           boolean showAnnotations = avm.getFromSubValArgOrPref(
                   Arg.SHOWANNOTATIONS, av.getSubVals(), null,
                   "SHOW_ANNOTATIONS", true);
-          af.setAnnotationsVisibility(showAnnotations, false, true);
 
-          // show temperature factor annotations?
-          if (avm.getBoolean(Arg.NOTEMPFAC))
+          boolean hideTFrows = (avm.getBoolean(Arg.NOTEMPFAC));
+          final AlignFrame _af = af;
+          // many of jalview's format/layout methods are only thread safe on the
+          // swingworker thread.
+          // all these methods should be on the alignViewController so it can
+          // coordinate such details
+          try
           {
-            // do this better (annotation types?)
-            List<String> hideThese = new ArrayList<>();
-            hideThese.add("Temperature Factor");
-            hideThese.add(AlphaFoldAnnotationRowBuilder.LABEL);
-            AlignmentUtils.showOrHideSequenceAnnotations(
-                    af.getCurrentView().getAlignment(), hideThese, null,
-                    false, false);
+            SwingUtilities.invokeAndWait(new Runnable()
+            {
+
+              @Override
+              public void run()
+              {
+                _af.setAnnotationsVisibility(showSSAnnotations, true,
+                        false);
+
+                _af.setAnnotationsVisibility(showAnnotations, false, true);
+
+                // show temperature factor annotations?
+                if (hideTFrows)
+                {
+                  // do this better (annotation types?)
+                  List<String> hideThese = new ArrayList<>();
+                  hideThese.add("Temperature Factor");
+                  hideThese.add(AlphaFoldAnnotationRowBuilder.LABEL);
+                  AlignmentUtils.showOrHideSequenceAnnotations(
+                          _af.getCurrentView().getAlignment(), hideThese,
+                          null, false, false);
+                }
+              }
+            });
+          } catch (Exception x)
+          {
+            Console.warn(
+                    "Unexpected exception adjusting annotation row visibility.",
+                    x);
           }
 
           // wrap alignment? do this last for formatting reasons
@@ -407,9 +445,11 @@ public class Commands
         {
           Console.debug(
                   "Opening '" + openFile + "' in existing alignment frame");
+
           DataSourceType dst = HttpUtils.startsWithHttpOrHttps(openFile)
                   ? DataSourceType.URL
                   : DataSourceType.FILE;
+
           FileLoader fileLoader = new FileLoader(!headless);
           fileLoader.LoadFile(af.getCurrentView(), openFile, dst, null,
                   false);
@@ -431,18 +471,22 @@ public class Commands
         }
       }
       if (progressBarSet && desktop != null)
+      {
         desktop.setProgressBar(null, progress);
-
+      }
     }
 
     // open the structure (from same PDB file or given PDBfile)
     if (!avm.getBoolean(Arg.NOSTRUCTURE))
     {
+
       AlignFrame af = afMap.get(id);
       if (avm.containsArg(Arg.STRUCTURE))
       {
         commandArgsProvided = true;
-        for (ArgValue av : avm.getArgValueList(Arg.STRUCTURE))
+        for (
+
+        ArgValue av : avm.getArgValueList(Arg.STRUCTURE))
         {
           argParser.setStructureFilename(null);
           String val = av.getValue();
@@ -679,6 +723,19 @@ public class Commands
                 BitmapImageSizing userBis = ImageMaker
                         .parseScaleWidthHeightStrings(scale, width, height);
 
+                /////
+                // DON'T TRY TO EXPORT IF VIEWER IS UNSUPPORTED
+                if (viewerType != ViewerType.JMOL)
+                {
+                  addWarn("Cannot export image for structure viewer "
+                          + viewerType.name() + " yet");
+                  continue;
+                }
+
+                /////
+                // Apply the temporary colourscheme to the linked alignment
+                // TODO: enhance for multiple linked alignments.
+
                 String imageColour = avm.getValueFromSubValOrArg(
                         structureImageArgValue, Arg.IMAGECOLOUR,
                         structureImageSubVals);
@@ -686,18 +743,8 @@ public class Commands
                         .getColourScheme(af);
                 this.colourAlignFrame(af, imageColour);
 
-                List<StructureCommandI> extraCommands = new ArrayList<>();
-                StructureCommandsI sc;
-                switch (viewerType)
-                {
-                case JMOL:
-                  sc = new JmolCommands();
-                  break;
-                default:
-                  addWarn("Cannot export image for structure viewer "
-                          + viewerType.name() + " yet");
-                  continue;
-                }
+                /////
+                // custom image background colour
 
                 String bgcolourstring = avm.getValueFromSubValOrArg(
                         structureImageArgValue, Arg.BGCOLOUR,
@@ -705,85 +752,97 @@ public class Commands
                 Color bgcolour = null;
                 if (bgcolourstring != null && bgcolourstring.length() > 0)
                 {
+                  bgcolour = ColorUtils.parseColourString(bgcolourstring);
+                  if (bgcolour == null)
+                  {
+                    Console.warn(
+                            "Background colour string '" + bgcolourstring
+                                    + "' not recognised -- using default");
+                  }
+                }
+
+                JalviewStructureDisplayI sview = structureViewer
+                        .getJalviewStructureDisplay();
+
+                File sessionToRestore = null;
+
+                List<StructureCommandI> extraCommands = new ArrayList<>();
+
+                if (extraCommands.size() > 0 || bgcolour != null)
+                {
                   try
                   {
-                    if (bgcolourstring.charAt(0) == '#')
-                    {
-                      bgcolour = Color.decode(bgcolourstring);
-                    }
-                    else
-                    {
-                      Field field = Color.class.getField(bgcolourstring);
-                      bgcolour = (Color) field.get(null);
-                    }
-                  } catch (IllegalArgumentException | NoSuchFieldException
-                          | SecurityException | IllegalAccessException nfe)
+                    sessionToRestore = sview.saveSession();
+                  } catch (Throwable t)
                   {
                     Console.warn(
-                            "Background colour string '" + bgcolourstring
-                                    + "' not recognised -- using black.");
-                    bgcolour = Color.black;
+                            "Unable to save temporary session file before custom structure view export operation.");
                   }
-                  extraCommands.add(sc.setBackgroundColour(bgcolour));
                 }
 
-                // TODO MAKE THIS VIEWER INDEPENDENT!!
-                switch (viewerType)
+                ////
+                // Do temporary ops
+
+                if (bgcolour != null)
+                {
+                  sview.getBinding().setBackgroundColour(bgcolour);
+                }
+
+                sview.getBinding().executeCommands(extraCommands, false,
+                        "Executing Custom Commands");
+
+                // and export the view as an image
+                boolean success = this.checksBeforeWritingToFile(avm,
+                        subVals, false, structureImageFilename,
+                        "structure image", isError);
+
+                if (!success)
                 {
-                case JMOL:
-                  JalviewStructureDisplayI sview = structureViewer
-                          .getJalviewStructureDisplay();
-                  JmolCommands jc = (JmolCommands) sc;
-                  if (sview instanceof AppJmol)
+                  continue;
+                }
+                Console.debug("Rendering image to " + structureImageFile);
+                //
+                // TODO - extend StructureViewer / Binding with makePDBImage so
+                // we can do this with every viewer
+                //
+
+                try
+                {
+                  // We don't expect class cast exception
+                  AppJmol jmol = (AppJmol) sview;
+                  jmol.makePDBImage(structureImageFile, imageType, renderer,
+                          userBis);
+                  Console.info("Exported structure image to "
+                          + structureImageFile);
+
+                  // RESTORE SESSION AFTER EXPORT IF NEED BE
+                  if (sessionToRestore != null)
                   {
-                    AppJmol jmol = (AppJmol) sview;
-                    JalviewJmolBinding jmb = (JalviewJmolBinding) jmol
-                            .getBinding();
-                    String state = new StringBuilder()
-                            .append("JalviewCommandsStructureState_")
-                            .append(viewerType.name()).toString();
-                    jmb.executeCommand(jc.saveState(state), false);
-                    for (StructureCommandI scmd : extraCommands)
-                    {
-                      jmb.executeCommand(scmd, false);
-                    }
-                    try
-                    {
-                      boolean success = this.checksBeforeWritingToFile(avm,
-                              subVals, false, structureImageFilename,
-                              "structure image", isError);
-                      if (!success)
-                      {
-                        continue;
-                      }
-
-                      Console.debug(
-                              "Rendering image to " + structureImageFile);
-                      jmol.makePDBImage(structureImageFile, imageType,
-                              renderer, userBis);
-                      Console.debug("Finished Rendering image to "
-                              + structureImageFile);
-
-                    } catch (ImageOutputException ioexc)
-                    {
-                      addError("Unexpected error whilst exporting image to "
-                              + structureImageFile, ioexc);
-                      isError = true;
-                      continue;
-                    } finally
-                    {
-                      jmb.executeCommand(jc.restoreState(state), false);
-                    }
+                    Console.debug(
+                            "Restoring session from " + sessionToRestore);
+
+                    sview.getBinding().restoreSession(
+                            sessionToRestore.getAbsolutePath());
 
                   }
-                  break;
-                default:
-                  // this shouldn't happen!
-                  addWarn("Cannot export image for structure viewer "
-                          + viewerType.name() + " yet");
+                } catch (ImageOutputException ioexec)
+                {
+                  addError(
+                          "Unexpected error when restoring structure viewer session after custom view operations.");
+                  isError = true;
                   continue;
+                } finally
+                {
+                  try
+                  {
+                    this.colourAlignFrame(af, originalColourScheme);
+                  } catch (Exception t)
+                  {
+                    addError(
+                            "Unexpected error when restoring colourscheme to alignment after temporary change for export.",
+                            t);
+                  }
                 }
-                this.colourAlignFrame(af, originalColourScheme);
               }
             }
           }
@@ -794,6 +853,7 @@ public class Commands
 
     if (wrap)
     {
+
       AlignFrame af = afMap.get(id);
       if (af != null)
       {
@@ -805,16 +865,16 @@ public class Commands
     boolean doShading = avm.getBoolean(Arg.TEMPFAC_SHADING);
     if (doShading)
     {
-      AlignFrame af = afMap.get(id);
-      for (AlignmentAnnotation aa : af.alignPanel.getAlignment()
-              .findAnnotation(PDBChain.class.getName().toString()))
-      {
-        AnnotationColourGradient acg = new AnnotationColourGradient(aa,
-                af.alignPanel.av.getGlobalColourScheme(), 0);
-        acg.setSeqAssociated(true);
-        af.changeColour(acg);
-        Console.info("Changed colour " + acg.toString());
-      }
+    AlignFrame af = afMap.get(id);
+    for (AlignmentAnnotation aa : af.alignPanel.getAlignment()
+            .findAnnotation(PDBChain.class.getName().toString()))
+    {
+      AnnotationColourGradient acg = new AnnotationColourGradient(aa,
+              af.alignPanel.av.getGlobalColourScheme(), 0);
+      acg.setSeqAssociated(true);
+      af.changeColour(acg);
+      Console.info("Changed colour " + acg.toString());
+    }
     }
     */
 
@@ -826,21 +886,29 @@ public class Commands
     ArgValuesMap avm = argParser.getLinkedArgs(id);
     AlignFrame af = afMap.get(id);
 
-    if (af == null)
+    if (avm != null && !avm.containsArg(Arg.GROOVY))
     {
-      addWarn("Did not have an alignment window for id=" + id);
+      // nothing to do
       return;
     }
 
+    if (af == null)
+    {
+      addWarn("Groovy script does not have an alignment window.  Proceeding with caution!");
+    }
+
     if (avm.containsArg(Arg.GROOVY))
     {
-      String groovyscript = avm.getValue(Arg.GROOVY);
-      if (groovyscript != null)
+      for (ArgValue groovyAv : avm.getArgValueList(Arg.GROOVY))
       {
-        // Execute the groovy script after we've done all the rendering stuff
-        // and before any images or figures are generated.
-        Console.info("Executing script " + groovyscript);
-        Jalview.getInstance().executeGroovyScript(groovyscript, af);
+        String groovyscript = groovyAv.getValue();
+        if (groovyscript != null)
+        {
+          // Execute the groovy script after we've done all the rendering stuff
+          // and before any images or figures are generated.
+          Console.info("Executing script " + groovyscript);
+          Jalview.getInstance().executeGroovyScript(groovyscript, af);
+        }
       }
     }
   }
@@ -850,9 +918,16 @@ public class Commands
     ArgValuesMap avm = argParser.getLinkedArgs(id);
     AlignFrame af = afMap.get(id);
 
+    if (avm != null && !avm.containsArg(Arg.IMAGE))
+    {
+      // nothing to do
+      return true;
+    }
+
     if (af == null)
     {
-      addWarn("Did not have an alignment window for id=" + id);
+      addWarn("Do not have an alignment window to create image from (id="
+              + id + ").  Not proceeding.");
       return false;
     }
 
@@ -979,9 +1054,16 @@ public class Commands
     ArgValuesMap avm = argParser.getLinkedArgs(id);
     AlignFrame af = afMap.get(id);
 
+    if (avm != null && !avm.containsArg(Arg.OUTPUT))
+    {
+      // nothing to do
+      return true;
+    }
+
     if (af == null)
     {
-      addWarn("Did not have an alignment window for id=" + id);
+      addWarn("Do not have an alignment window (id=" + id
+              + ").  Not proceeding.");
       return false;
     }