Merge branch 'JAL-1551_2_11_3_spotlett' into features/JAL-4134_use_annotation_row_for...
authorJames Procter <j.procter@dundee.ac.uk>
Mon, 22 May 2023 08:54:37 +0000 (09:54 +0100)
committerJames Procter <j.procter@dundee.ac.uk>
Mon, 22 May 2023 08:54:37 +0000 (09:54 +0100)
32 files changed:
build.gradle
help/help/help.jhm
help/help/helpTOC.xml
help/help/html/features/clarguments-old.html
src/jalview/bin/ArgsParser.java
src/jalview/bin/Commands.java
src/jalview/bin/Jalview.java
src/jalview/bin/Launcher.java
src/jalview/bin/argparser/Arg.java
src/jalview/bin/argparser/ArgParser.java
src/jalview/bin/argparser/ArgValue.java
src/jalview/bin/argparser/ArgValues.java
src/jalview/bin/argparser/BootstrapArgs.java
src/jalview/fts/core/GFTSPanel.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GCutAndPasteHtmlTransfer.java
src/jalview/jbgui/GCutAndPasteTransfer.java
src/jalview/jbgui/GPCAPanel.java
src/jalview/jbgui/GRnaStructureViewer.java
src/jalview/jbgui/GSplitFrame.java
src/jalview/jbgui/GStructureViewer.java
src/jalview/jbgui/GTreePanel.java
src/jalview/util/Platform.java
src/jalview/util/StringUtils.java
test/jalview/bin/CommandsTest2.java
test/jalview/bin/argparser/ArgParserTest.java
utils/debian/debian/jalview-mailcap
utils/debian/debian/wrappers/jalview
utils/eclipse/jalview
utils/eclipse/launcher [deleted file]
utils/getdown/bin/jalview.ps1
utils/getdown/bin/jalview.sh

index 6a622c3..ad7c94f 100644 (file)
@@ -2237,8 +2237,8 @@ task getdownWebsite() {
       props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
       props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
       props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
-      props.put("getdown_txt_ui.icon", "${getdownImagesBuildDir}/${getdown_icon}")
-      props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesBuildDir}/${getdown_mac_dock_icon}")
+      props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
+      props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
     }
 
     props.put("getdown_txt_title", jalview_name)
index 8e609a6..0c39a52 100755 (executable)
@@ -69,6 +69,7 @@
    <mapID target="clarguments-basic" url="html/features/clarguments-basic.html"/>
    <mapID target="clarguments-advanced" url="html/features/clarguments-advanced.html"/>
    <mapID target="clarguments-argfiles" url="html/features/clarguments-argfiles.html"/>
+   <mapID target="clarguments-old" url="html/features/clarguments-old.html"/>
    <mapID target="jvlfiles" url="html/features/jvlfiles.html"/>
    <mapID target="io" url="html/io/index.html"/>
    <mapID target="io.modellerpir" url="html/io/modellerpir.html"/>
index 433139d..f6bb7c7 100755 (executable)
                        <tocitem text="Command Line: basic usage" target="clarguments-basic" />
                        <tocitem text="Command Line: advanced usage" target="clarguments-advanced" />
                        <tocitem text="Command Line: argument files" target="clarguments-argfiles" />
+                       <tocitem text="Command Line: old command line arguments" target="clarguments-old" />
                </tocitem>
                <tocitem text="Memory Settings" target="memory" expand="false"/>
                <tocitem text="Jalview Launch Files" target="jvlfiles" expand="false"/>
index c0f4c42..3d90a9e 100644 (file)
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  -->
-<title>Jalview Command Line Arguments</title>
+<title>Jalview Command Line Arguments (pre 2.11.3.0)</title>
 <body>
   <p>
-    <strong>The Jalview Executable's Command Line Arguments</strong>
+    <strong>The Jalview Executable's Command Line Arguments (pre 2.11.3.0)</strong>
   </p>
+
+  <table border="1">
+  <tr><td>
+  <em>
+  This page describes the command line arguments used pre 2.11.3.0 (released mid 2023).
+  <br/>
+  Whilst these arguments will be supported for some versions after 2.11.3.0, they are now deprecated
+  and will be removed at some time in the future.
+  </em>
+  </td></tr>
+  </table>
+
+
   See
   <a href="commandline.html">running Jalview from the command line</a>
   for more information.
index c927f1f..ab33542 100644 (file)
@@ -42,7 +42,7 @@ public class ArgsParser
     for (int i = 0; i < args.length; i++)
     {
       String arg = args[i].trim();
-      if (arg.charAt(0) == '-')
+      if (arg.length() > 0 && arg.charAt(0) == '-')
       {
         arg = arg.substring(1);
       }
index 50ff7c3..dcdb4c0 100644 (file)
@@ -561,6 +561,9 @@ public class Commands
           StructureChooser.openStructureFileForSequence(null, null, ap, seq,
                   false, structureFilepath, tft, paeFilepath, false,
                   ssFromStructure, false, viewerType);
+
+          String structureImage = ArgParser.getValueFromSubValOrArg(avm, av,
+                  Arg.STRUCTUREIMAGE, subVals);
         }
       }
     }
index cf73c81..59382fd 100755 (executable)
@@ -49,6 +49,7 @@ import java.util.Vector;
 import java.util.logging.ConsoleHandler;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.stream.Collectors;
 
 import javax.swing.JDialog;
 import javax.swing.JFrame;
@@ -68,6 +69,7 @@ import groovy.lang.Binding;
 import groovy.util.GroovyScriptEngine;
 import jalview.bin.argparser.Arg;
 import jalview.bin.argparser.Arg.Opt;
+import jalview.bin.argparser.Arg.Type;
 import jalview.bin.argparser.ArgParser;
 import jalview.bin.argparser.BootstrapArgs;
 import jalview.ext.so.SequenceOntology;
@@ -300,8 +302,11 @@ public class Jalview
       System.setSecurityManager(null);
     }
 
-    if (args == null)
+    if (args == null || args.length == 0 || (args.length == 1
+            && (args[0] == null || args[0].length() == 0)))
+    {
       args = new String[] {};
+    }
 
     // get args needed before proper ArgParser
     bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
@@ -344,6 +349,7 @@ public class Jalview
       final File appdir = new File(appdirString);
       new Thread()
       {
+
         @Override
         public void run()
         {
@@ -473,7 +479,7 @@ public class Jalview
     if (bootstrapArgs.contains(Arg.ARGFILE))
     {
       argparser = ArgParser.parseArgFiles(
-              bootstrapArgs.getList(Arg.ARGFILE),
+              bootstrapArgs.getValueList(Arg.ARGFILE),
               bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
               bootstrapArgs);
     }
@@ -493,7 +499,10 @@ public class Jalview
     {
       if (bootstrapArgs.contains(Arg.HELP))
       {
-        System.out.println(Arg.usage());
+        List<Map.Entry<Type, String>> helpArgs = bootstrapArgs
+                .getList(Arg.HELP);
+        System.out.println(Arg.usage(helpArgs.stream().map(e -> e.getKey())
+                .collect(Collectors.toList())));
         Jalview.exit(null, 0);
       }
       if (aparser.contains("help") || aparser.contains("h"))
@@ -547,7 +556,7 @@ public class Jalview
     List<String> setprops = new ArrayList<>();
     if (bootstrapArgs.contains(Arg.SETPROP))
     {
-      setprops = bootstrapArgs.getList(Arg.SETPROP);
+      setprops = bootstrapArgs.getValueList(Arg.SETPROP);
     }
     else
     {
@@ -1696,14 +1705,40 @@ public class Jalview
 
   public static void exit(String message, int exitcode)
   {
-    Console.debug("Using Jalview.exit");
-    if (message != null)
-      if (exitcode == 0)
-        Console.info(message);
-      else
-        Console.error(message);
+    if (Console.log == null)
+    {
+      // Don't start the logger just to exit!
+      if (message != null)
+      {
+        if (exitcode == 0)
+        {
+          System.out.println(message);
+        }
+        else
+        {
+          System.err.println(message);
+        }
+      }
+    }
+    else
+    {
+      Console.debug("Using Jalview.exit");
+      if (message != null)
+      {
+        if (exitcode == 0)
+        {
+          Console.info(message);
+        }
+        else
+        {
+          Console.error(message);
+        }
+      }
+    }
     if (exitcode > -1)
+    {
       System.exit(exitcode);
+    }
   }
 
   /*
index 61b87e3..f29e0a1 100644 (file)
@@ -26,6 +26,7 @@ import java.lang.management.ManagementFactory;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.TimeUnit;
 
 import jalview.util.ChannelProperties;
 import jalview.util.LaunchUtils;
@@ -90,9 +91,46 @@ public class Launcher
 
     String jvmmempc = null;
     String jvmmemmax = null;
+    boolean debug = false;
+    boolean wait = true;
+    boolean quiet = false;
+    // must set --debug before --launcher...
+    boolean launcherstop = false;
+    boolean launcherprint = false;
+    boolean launcherwait = false;
     ArrayList<String> arguments = new ArrayList<>();
     for (String arg : args)
     {
+      if (arg.equals("--debug"))
+      {
+        debug = true;
+      }
+      if (arg.equals("--quiet"))
+      {
+        quiet = true;
+      }
+      if (debug && arg.equals("--launcherprint"))
+      {
+        launcherprint = true;
+      }
+      if (debug && arg.equals("--launcherstop"))
+      {
+        launcherstop = true;
+      }
+      if (debug && arg.equals("--launcherwait"))
+      {
+        launcherwait = true;
+      }
+      // this ends the launcher immediately
+      if (debug && arg.equals("--launchernowait"))
+      {
+        wait = false;
+      }
+      // Don't add the --launcher... args to Jalview launch
+      if (arg.startsWith("--launcher"))
+      {
+        continue;
+      }
       // jvmmempc and jvmmemmax args used to set memory and are not passed on to
       // startClass
       if (arg.startsWith(
@@ -146,7 +184,7 @@ public class Launcher
       }
     }
 
-    // add memory setting if not specified
+    // add these settings if not already specified
     boolean memSet = false;
     boolean dockIcon = false;
     boolean dockName = false;
@@ -208,7 +246,7 @@ public class Launcher
     String scalePropertyArg = HiDPISetting.getScalePropertyArg();
     if (scalePropertyArg != null)
     {
-      System.out.println("Running " + startClass + " with scale setting "
+      sysout(debug, quiet, "Running " + startClass + " with scale setting "
               + scalePropertyArg);
       command.add(scalePropertyArg);
     }
@@ -218,18 +256,21 @@ public class Launcher
 
     final ProcessBuilder builder = new ProcessBuilder(command);
 
-    if (Boolean.parseBoolean(System.getProperty("launcherprint", "false")))
+    if ((Boolean.parseBoolean(System.getProperty("launcherprint", "false"))
+            || launcherprint))
     {
-      System.out.println(
+      sysout(debug, quiet,
               "LAUNCHER COMMAND: " + String.join(" ", builder.command()));
     }
-    System.out.println("Running " + startClass + " with "
-            + (memSetting == null ? "no memory setting"
-                    : ("memory setting " + memSetting)));
+    sysout(debug, quiet,
+            "Running " + startClass + " with "
+                    + (memSetting == null ? "no memory setting"
+                            : ("memory setting " + memSetting)));
 
-    if (Boolean.parseBoolean(System.getProperty("launcherstop", "false")))
+    if (Boolean.parseBoolean(System.getProperty("launcherstop", "false"))
+            || (debug && launcherstop))
     {
-      System.out.println(
+      sysout(debug, quiet,
               "System property 'launcherstop' is set and not 'false'. Exiting.");
       System.exit(0);
     }
@@ -237,12 +278,24 @@ public class Launcher
     {
       builder.inheritIO();
       Process process = builder.start();
-      process.waitFor();
+      if (wait || launcherwait)
+      {
+        sysout(debug, quiet, "Launching application process");
+        process.waitFor();
+      }
+      else
+      {
+        int waitInt = 0;
+        sysout(debug, quiet,
+                "Wait time for application process is " + waitInt + "ms");
+        process.waitFor(waitInt, TimeUnit.MILLISECONDS);
+      }
+      sysout(debug, quiet, "Launcher process ending");
     } catch (IOException e)
     {
       if (e.getMessage().toLowerCase(Locale.ROOT).contains("memory"))
       {
-        System.out.println("Caught a memory exception: " + e.getMessage());
+        System.err.println("Caught a memory exception: " + e.getMessage());
         // Probably the "Cannot allocate memory" error, try without the memory
         // setting
         ArrayList<String> commandNoMem = new ArrayList<>();
@@ -255,7 +308,7 @@ public class Launcher
         }
         final ProcessBuilder builderNoMem = new ProcessBuilder(
                 commandNoMem);
-        System.out.println("Command without memory setting: "
+        System.err.println("Command without memory setting: "
                 + String.join(" ", builderNoMem.command()));
         try
         {
@@ -277,4 +330,12 @@ public class Launcher
     }
   }
 
+  private static void sysout(boolean debug, boolean quiet, String message)
+  {
+    if (debug && !quiet)
+    {
+      System.out.println("LAUNCHERDEBUG - " + message);
+    }
+  }
+
 }
index a18057c..ae01c48 100644 (file)
@@ -2,65 +2,80 @@ package jalview.bin.argparser;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.stream.Collectors;
 
-import jalview.bin.Cache;
+import jalview.bin.argparser.Arg.Opt;
 import jalview.util.ChannelProperties;
+import jalview.util.Platform;
 
 public enum Arg
 {
 
   // Initialising arguments (BOOTSTRAP)
-  HELP("h", "Display this help statement", Opt.UNARY, Opt.BOOTSTRAP),
-  VERSION("v",
+  HELP(Type.HELP, "h", "Display basic help", Opt.UNARY, Opt.BOOTSTRAP,
+          Opt.HASTYPE, Opt.MULTI),
+  /*
+   * Other --help-type Args will be added by the static block.
+   */
+  VERSION(Type.CONFIG, "v",
           "Display the version of "
                   + ChannelProperties.getProperty("app_name"),
           Opt.UNARY, Opt.BOOTSTRAP),
-  HEADLESS(
+  HEADLESS(Type.CONFIG,
           "Run Jalview in headless mode. No GUI interface will be created and Jalview will quit after all arguments have been processed.",
           Opt.UNARY, Opt.BOOTSTRAP),
-  JABAWS("Set a different URL to connect to a JABAWS server.", Opt.STRING,
-          Opt.BOOTSTRAP),
-  NEWS("Show (or don't show) the news feed.", true, Opt.BOOLEAN,
-          Opt.BOOTSTRAP),
-  SPLASH("Show (or don't show) the About Jalview splash screen.", true,
+  JABAWS(Type.CONFIG, "Set a different URL to connect to a JABAWS server.",
+          Opt.STRING, Opt.BOOTSTRAP),
+  NEWS(Type.CONFIG, "Show (or don't show) the news feed.", true,
+          Opt.BOOLEAN, Opt.BOOTSTRAP),
+  SPLASH(Type.CONFIG,
+          "Show (or don't show) the About Jalview splash screen.", true,
           Opt.BOOLEAN, Opt.BOOTSTRAP),
-  QUESTIONNAIRE(
+  QUESTIONNAIRE(Type.CONFIG,
           "Show (or don't show) the questionnaire if one is available.",
           true, Opt.BOOLEAN, Opt.BOOTSTRAP),
-  USAGESTATS("Send (or don't send) initial launch usage stats.", true,
+  USAGESTATS(Type.CONFIG,
+          "Send (or don't send) initial launch usage stats.", true,
           Opt.BOOLEAN, Opt.BOOTSTRAP),
-  WEBSERVICEDISCOVERY(
+  WEBSERVICEDISCOVERY(Type.CONFIG,
           "Attempt (or don't attempt) to connect to JABAWS web services.",
           true, Opt.BOOLEAN, Opt.BOOTSTRAP),
-  PROPS("Use a file as the preferences file instead of the usual ~/"
-          + ChannelProperties.getProperty("preferences.filename")
-          + " file.", Opt.STRING, Opt.BOOTSTRAP),
-  DEBUG("d", "Start Jalview in debug log level.", Opt.BOOLEAN,
+  PROPS(Type.CONFIG,
+          "Use a file as the preferences file instead of the usual ~/"
+                  + ChannelProperties.getProperty("preferences.filename")
+                  + " file.",
+          Opt.STRING, Opt.BOOTSTRAP),
+  DEBUG(Type.CONFIG, "d", "Start Jalview in debug log level.", Opt.BOOLEAN,
           Opt.BOOTSTRAP),
-  TRACE("Start Jalview in trace log level.", Opt.BOOLEAN, Opt.BOOTSTRAP,
-          Opt.SECRET),
-  QUIET("q",
+  TRACE(Type.CONFIG, "Start Jalview in trace log level.", Opt.BOOLEAN,
+          Opt.BOOTSTRAP, Opt.SECRET),
+  QUIET(Type.CONFIG, "q",
           "Stop all output to STDOUT (after the Java Virtual Machine has started). Use â€‘‑quiet a second time to stop all output to STDERR.",
           Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP),
-  INITSUBSTITUTIONS(
+  INITSUBSTITUTIONS(Type.CONFIG,
           "Set â€‘‑substitutions to be initially enabled (or initially disabled).",
-          true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION),
+          true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION, Opt.SECRET),
 
   // Opening an alignment
-  OPEN("Opens one or more alignment files or URLs in new alignment windows.",
+  OPEN(Type.OPENING,
+          "Opens one or more alignment files or URLs in new alignment windows.",
           Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER, Opt.MULTI,
-          Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.STORED),
-  APPEND("Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
+          Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.STORED,
+          Opt.PRIMARY),
+  APPEND(Type.OPENING,
+          "Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
           Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
-          Opt.ALLOWSUBSTITUTIONS, Opt.INPUT),
-  TITLE("Specifies the title for the open alignment window as string.",
+          Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.PRIMARY),
+  TITLE(Type.OPENING,
+          "Specifies the title for the open alignment window as string.",
           Opt.STRING, Opt.LINKED),
-  COLOUR("color", // being a bit soft on the Americans!
+  COLOUR(Type.OPENING, "color", // being a bit soft on the Americans!
           "Applies the colour scheme to the open alignment window. Valid values are:\n"
                   + "clustal,\n" + "blosum62,\n" + "pc-identity,\n"
                   + "zappo,\n" + "taylor,\n" + "gecos-flower,\n"
@@ -72,144 +87,174 @@ public enum Arg
                   + "purine-pyrimidine,\n" + "rna-helices,\n"
                   + "t-coffee-scores,\n" + "sequence-id.",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  FEATURES("Add a feature file or URL to the open alignment.", Opt.STRING,
-          Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
-  TREE("Add a tree file or URL to the open alignment.", Opt.STRING,
-          Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
-  SORTBYTREE(
+  FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
+          Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
+  TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
+          Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
+  SORTBYTREE(Type.OPENING,
           "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
           true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWALL),
-  ANNOTATIONS("Add an annotations file or URL to the open alignment.",
+  ANNOTATIONS(Type.OPENING,
+          "Add an annotations file or URL to the open alignment.",
           Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
-  SHOWANNOTATIONS(
+  SHOWANNOTATIONS(Type.OPENING,
           "Enforces showing (or not showing) alignment annotations.",
           Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
-  WRAP("Enforces wrapped (or not wrapped) alignment formatting.",
+  WRAP(Type.OPENING,
+          "Enforces wrapped (or not wrapped) alignment formatting.",
           Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
-  NOSTRUCTURE(
+  NOSTRUCTURE(Type.OPENING,
           "Do not open or process any 3D structure in the â€‘‑open or â€‘‑append files.",
           Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
 
   // Adding a 3D structure
-  STRUCTURE(
+  STRUCTURE(Type.STRUCTURE,
           "Load a structure file or URL associated with a sequence in the open alignment.\n"
                   + "The sequence to be associated with can be specified with a following --seqid argument, or the subval modifier seqid=ID can be used. A subval INDEX can also be used to specify the INDEX-th sequence in the open alignment.",
+          Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
+          Opt.PRIMARY),
+  SEQID(Type.STRUCTURE,
+          "Specify the sequence name for the preceding --structure to be associated with.",
           Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
-  SEQID("Specify the sequence name for the preceding --structure to be associated with.",
-          Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
-  PAEMATRIX("Add a PAE json matrix file to the preceding --structure.",
+  PAEMATRIX(Type.STRUCTURE,
+          "Add a PAE json matrix file to the preceding --structure.",
           Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
-  TEMPFAC("Set the type of temperature factor. Possible values are:\n"
-          + "default,\n" + "plddt.", Opt.STRING, Opt.LINKED),
-  STRUCTUREVIEWER(
-          "Set the structure viewer to use to open the 3d structure file specified in previous --structure to name. Possible values of name are:\n"
+  TEMPFAC(Type.STRUCTURE,
+          "Set the type of temperature factor. Possible values are:\n"
+                  + "default,\n" + "plddt.",
+          Opt.STRING, Opt.LINKED),
+  STRUCTUREVIEWER(Type.STRUCTURE,
+          "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values of name are:\n"
                   + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
                   + "pymol.",
           Opt.STRING, Opt.LINKED, Opt.MULTI),
-  NOTEMPFAC(
+  STRUCTUREIMAGE(Type.STRUCTURE,
+          "Export an image of a 3D structure opened in JMOL", Opt.STRING,
+          Opt.LINKED, Opt.MULTI),
+  NOTEMPFAC(Type.STRUCTURE,
           "Do not show the temperature factor annotation for the preceding --structure.",
           Opt.UNARY, Opt.LINKED, Opt.ALLOWALL, Opt.SECRET), // keep this secret
                                                             // until it
   // works!
-  SHOWSSANNOTATIONS(null, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
+  SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
+          Opt.ALLOWALL),
 
   // Outputting files
-  IMAGE("Output an image of the open alignment window. Format is specified by the subval modifier, a following --type argument or guessed from the file extension. Valid formats/extensions are:\n"
-          + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
+  IMAGE(Type.IMAGE,
+          "Output an image of the open alignment window. Format is specified by the subval modifier, a following --type argument or guessed from the file extension. Valid formats/extensions are:\n"
+                  + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
           Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
-          Opt.REQUIREINPUT, Opt.OUTPUT),
-  TYPE("Set the image format for the preceding --image to name. Valid values for name are: svg,\n"
-          + "png,\n" + "eps,\n" + "html,\n" + "biojs.", Opt.STRING,
-          Opt.LINKED, Opt.ALLOWALL),
-  TEXTRENDERER(
+          Opt.REQUIREINPUT, Opt.OUTPUT, Opt.PRIMARY),
+  TYPE(Type.IMAGE,
+          "Set the image format for the preceding --image to name. Valid values for name are: svg,\n"
+                  + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
+          Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
+  TEXTRENDERER(Type.IMAGE,
           "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values for name are:\n"
                   + "text,\n" + "lineart.",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  SCALE("Sets a scaling for bitmap image format (PNG). Should be given as a floating point number. If used in conjunction with --width and --height then the smallest scaling will be used (scale, width and height provide bounds for the image).",
+  SCALE(Type.IMAGE,
+          "Sets a scaling for bitmap image format (PNG). Should be given as a floating point number. If used in conjunction with --width and --height then the smallest scaling will be used (scale, width and height provide bounds for the image).",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  WIDTH("Sets a width for bitmap image format (PNG) with the height maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --scale and --height then the smallest scaling will be used (scale, width and height provide bounds for the image).",
+  WIDTH(Type.IMAGE,
+          "Sets a width for bitmap image format (PNG) with the height maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --scale and --height then the smallest scaling will be used (scale, width and height provide bounds for the image).",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  HEIGHT("Sets a height for bitmap image format (PNG) with the width maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --scale and --width then the smallest scaling will be used (scale, width and height provide bounds for the image).",
+  HEIGHT(Type.IMAGE,
+          "Sets a height for bitmap image format (PNG) with the width maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --scale and --width then the smallest scaling will be used (scale, width and height provide bounds for the image).",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  OUTPUT("Export the open alignment to file filename. The format name is specified by the subval modifier format=name, a following --format name argument or guessed from the file extension. Valid format names (and file extensions) are:\n"
-          + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
-          + "stockholm (sto, stk),\n" + "pir (pir),\n" + "blc (blc),\n"
-          + "amsa (amsa),\n" + "json (json),\n" + "pileup (pileup),\n"
-          + "msf (msf),\n" + "clustal (aln),\n" + "phylip (phy),\n"
-          + "jalview (jvp, jar).", Opt.STRING, Opt.LINKED,
-          Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL, Opt.REQUIREINPUT,
-          Opt.OUTPUT),
-  FORMAT("Sets the format for the preceding --output file. Valid formats are:\n"
-          + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n" + "blc,\n"
-          + "amsa,\n" + "json,\n" + "pileup,\n" + "msf,\n" + "clustal,\n"
-          + "phylip,\n" + "jalview.", Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  GROOVY("Process a groovy script in the file for the open alignment.",
+  OUTPUT(Type.OUTPUT,
+          "Export the open alignment to file filename. The format name is specified by the subval modifier format=name, a following --format name argument or guessed from the file extension. Valid format names (and file extensions) are:\n"
+                  + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
+                  + "stockholm (sto, stk),\n" + "pir (pir),\n"
+                  + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
+                  + "pileup (pileup),\n" + "msf (msf),\n"
+                  + "clustal (aln),\n" + "phylip (phy),\n"
+                  + "jalview (jvp, jar).",
+          Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
+          Opt.REQUIREINPUT, Opt.OUTPUT, Opt.PRIMARY),
+  FORMAT(Type.OUTPUT,
+          "Sets the format for the preceding --output file. Valid formats are:\n"
+                  + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
+                  + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
+                  + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
+          Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
+  GROOVY(Type.PROCESS,
+          "Process a groovy script in the file for the open alignment.",
           Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
           Opt.ALLOWALL),
-  BACKUPS("Enable (or disable) writing backup files when saving an â€‘‑output file. This applies to the current open alignment.  To apply to all â€‘‑output and â€‘‑image files, use after â€‘‑all.",
+  BACKUPS(Type.OUTPUT,
+          "Enable (or disable) writing backup files when saving an â€‘‑output file. This applies to the current open alignment.  To apply to all â€‘‑output and â€‘‑image files, use after â€‘‑all.",
           true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
-  OVERWRITE(
+  OVERWRITE(Type.OUTPUT,
           "Enable (or disable) overwriting of output files without backups enabled. This applies to the current open alignment.  To apply to all â€‘‑output and â€‘‑image files, use after â€‘‑all.",
           Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
-  CLOSE("Close the current open alignment window. This occurs after other output arguments. This applies to the current open alignment.  To apply to all â€‘‑output and â€‘‑image files, use after â€‘‑all.",
+  CLOSE(Type.OPENING,
+          "Close the current open alignment window. This occurs after other output arguments. This applies to the current open alignment.  To apply to all â€‘‑output and â€‘‑image files, use after â€‘‑all.",
           Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
 
   // controlling flow of arguments
-  NEW("Move on to a new alignment window. This will ensure --append will start a new alignment window and other linked arguments will apply to the new alignment window.",
+  NEW(Type.FLOW,
+          "Move on to a new alignment window. This will ensure --append will start a new alignment window and other linked arguments will apply to the new alignment window.",
           Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
-  SUBSTITUTIONS(
-          "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are {basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
-                  + "{dirname}, - the directory (folder) name of the currently --opened file (or first --appended file),\n"
+  SUBSTITUTIONS(Type.FLOW,
+          "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
+                  + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
+                  + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
                   + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
                   + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
                   + "{n} - the value of the index counter (starting at 0).\n"
                   + "{++n} - increase and substitute the value of the index counter,\n"
                   + "{} - the value of the current alignment window default index.",
           true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
-  ARGFILE("Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
-          + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
-          + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
+  ARGFILE(Type.FLOW,
+          "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
+                  + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
+                  + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
           Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
           Opt.ALLOWSUBSTITUTIONS),
-  NPP("n++",
+  NPP(Type.FLOW, "n++",
           "Increase the index counter used in argument value substitutions.",
           Opt.UNARY, Opt.MULTI, Opt.NOACTION),
-  ALL("Apply the following output arguments to all sets of linked arguments.",
+  ALL(Type.FLOW,
+          "Apply the following output arguments to all sets of linked arguments.",
           Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
-  OPENED("Apply the following output arguments to all of the last --open'ed set of linked arguments.",
+  OPENED(Type.FLOW,
+          "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
           Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
-  QUIT("After all files have been opened, appended and output, quit Jalview. In â€‘‑headless mode this already happens.",
+  QUIT(Type.FLOW,
+          "After all files have been opened, appended and output, quit Jalview. In â€‘‑headless mode this already happens.",
           Opt.UNARY),
 
   // secret options
-  TESTOUTPUT(
+  TESTOUTPUT(Type.CONFIG,
           "Allow specific stdout information.  For testing purposes only.",
           Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
-  SETPROP("Set an individual Java System property.", Opt.STRING, Opt.MULTI,
-          Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
-  NIL("This argument does nothing on its own, but can be used with linkedIds.",
+  SETPROP(Type.CONFIG, "Set an individual Java System property.",
+          Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
+  NIL(Type.FLOW,
+          "This argument does nothing on its own, but can be used with linkedIds.",
           Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
 
   // private options (inserted during arg processing)
-  SETARGFILE(
+  SETARGFILE(Type.FLOW,
           "Sets the current value of the argfilename.  Inserted before argfilecontents.",
           Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
           Opt.NOACTION),
-  UNSETARGFILE(
+  UNSETARGFILE(Type.FLOW,
           "Unsets the current value of the argfilename.  Inserted after argfile contents.",
           Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
 
   // these last two have no purpose in the normal Jalview application but are
   // used by jalview.bin.Launcher to set memory settings. They are not used by
   // argparser but are here for Usage statement reasons.
-  JVMMEMPC(
+  JVMMEMPC(Type.CONFIG,
           "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
                   + "The equals sign (\"=\") separator must be used with no spaces.",
-          Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING),
-  JVMMEMMAX(
+          Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
+  JVMMEMMAX(Type.CONFIG,
           "Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected.\n"
                   + "The equals sign (\"=\") separator must be used with no spaces.",
-          Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING),
+          Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
 
   ;
 
@@ -260,32 +305,75 @@ public enum Arg
     OUTPUT, // This Arg provides an output filename. With Opt.ALLOWALL *.ext is
             // shorthand for --all --output={basename}.ext
     STORED, // This Arg resets and creates a new set of "opened" linkedIds
+    HELP, // This Arg is a --help type arg
+    PRIMARY, // This Arg is the main Arg for its type
+    HASTYPE, // This Arg can have an Arg.Type assigned to it (and no value)
+    FIRST, // Move this arg to the first in usage statement (within type)
+    LAST, // Move this arg to the end in usage statement (within type)
+  }
+
+  public static enum Type
+  {
+    // Type restricts argument to certain usage output
+    HELP, // --help
+    CONFIG("arguments used to configure "
+            + ChannelProperties.getProperty("app_name") + " from startup"),
+    OPENING("arguments used to open and format alignments"),
+    STRUCTURE("arguments used to add and format 3D structure data"),
+    PROCESS("arguments used to process an alignment once opened"),
+    OUTPUT("arguments used to save data from a processed alignment"),
+    IMAGE("arguments used to export an image of an alignment"),
+    FLOW("arguments that control processing of the other arguments"), //
+    ALL("all arguments"), // mostly just a place-holder for --help-all
+    NONE, // mostly a place-holder for --help
+    INVALID;
+
+    private String description;
+
+    private Type()
+    {
+      description = null;
+    }
+
+    private Type(String description)
+    {
+      this.description = description;
+    }
+
+    public String description()
+    {
+      return description;
+    }
   }
 
   private final String[] argNames;
 
   private Opt[] argOptions;
 
-  private boolean defaultBoolValue = false;
+  private boolean defaultBoolValue;
 
-  private String description = null;
+  private String description;
 
-  private Arg(String description, Opt... options)
+  private Type type;
+
+  private Arg(Type type, String description, Opt... options)
   {
-    this(null, description, false, options);
+    this(type, null, description, false, options);
   }
 
-  private Arg(String description, boolean defaultBoolean, Opt... options)
+  private Arg(Type type, String description, boolean defaultBoolean,
+          Opt... options)
   {
-    this(null, description, defaultBoolean, options);
+    this(type, null, description, defaultBoolean, options);
   }
 
-  private Arg(String alternativeName, String description, Opt... options)
+  private Arg(Type type, String alternativeName, String description,
+          Opt... options)
   {
-    this(alternativeName, description, false, options);
+    this(type, alternativeName, description, false, options);
   }
 
-  private Arg(String alternativeName, String description,
+  private Arg(Type type, String alternativeName, String description,
           boolean defaultBoolean, Opt... options)
   {
     this.argNames = alternativeName != null
@@ -293,6 +381,7 @@ public enum Arg
             { this.getName(), alternativeName }
             : new String[]
             { this.getName() };
+    this.type = type;
     this.description = description;
     this.defaultBoolValue = defaultBoolean;
     this.setOptions(options);
@@ -328,6 +417,7 @@ public enum Arg
     if (getNames().length > 0)
       sb.append('"');
     sb.append(")\n");
+    sb.append("\nType: " + type.name());
     sb.append("\nOpt: ");
     // map List<Opt> to List<String> for the String.join
     List<String> optList = Arrays.asList(argOptions).stream()
@@ -365,6 +455,16 @@ public enum Arg
     return false;
   }
 
+  public boolean hasAllOptions(Opt... opts)
+  {
+    for (Opt o : opts)
+    {
+      if (!this.hasOption(o))
+        return false;
+    }
+    return true;
+  }
+
   protected void setOptions(Opt... options)
   {
     this.argOptions = options;
@@ -375,6 +475,11 @@ public enum Arg
     return defaultBoolValue;
   }
 
+  public Type getType()
+  {
+    return this.type;
+  }
+
   protected String getDescription()
   {
     return description;
@@ -393,123 +498,347 @@ public enum Arg
 
   public static final String usage()
   {
-    StringBuilder sb = new StringBuilder();
+    return usage(null);
+  }
 
-    sb.append(ChannelProperties.getProperty("app_name"));
-    String version = Cache.getDefault("VERSION", null);
-    if (version != null)
+  public static final void appendUsageGeneral(StringBuilder sb,
+          int maxArgLength)
+  {
+    for (Type t : EnumSet.allOf(Type.class))
     {
-      sb.append(" version ");
-      sb.append(Cache.getDefault("VERSION", "unknown"));
+      if (t.description() != null)
+      {
+        StringBuilder argSb = new StringBuilder();
+        argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
+                .append(t.name().toLowerCase(Locale.ROOT));
+        appendArgAndDescription(sb, argSb.toString(),
+                "Help for " + t.description(), null, maxArgLength);
+        sb.append(System.lineSeparator());
+      }
     }
-    sb.append(System.lineSeparator());
-    sb.append("Usage: jalview [files...] [args]");
+  }
+
+  public static final String usage(List<Type> types)
+  {
+    StringBuilder sb = new StringBuilder();
+
+    sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
+            + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
+            + "] file(s)] [args]");
     sb.append(System.lineSeparator());
     sb.append(System.lineSeparator());
 
-    int maxArgLength = 0;
-    for (Arg a : EnumSet.allOf(Arg.class))
+    if (types == null || types.contains(null))
     {
-      if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
-        continue;
-
-      StringBuilder argSb = new StringBuilder();
-      argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
-              : a.argString());
-      if (a.hasOption(Opt.STRING))
-        argSb.append("=value");
-      if (argSb.length() > maxArgLength)
-        maxArgLength = argSb.length();
-    }
+      // always show --help
+      appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
+              DESCRIPTIONINDENT);
+      sb.append(System.lineSeparator());
 
-    // might want to sort these
-    for (Arg a : EnumSet.allOf(Arg.class))
+      appendUsageGeneral(sb, DESCRIPTIONINDENT);
+    }
+    else
     {
-      if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
-        continue;
-      StringBuilder argSb = new StringBuilder();
-      argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
-              : a.argString());
-      if (a.hasOption(Opt.STRING))
-        argSb.append("=value");
-      Iterator<String> descLines = null;
-      if (a.getDescription() != null)
+      List<Arg> args = argsSortedForDisplay(types);
+
+      /*
+       * just use a set maxArgLength of DESCRIPTIONINDENT
+       
+      int maxArgLength = 0;
+      for (Arg a : args)
+      {
+        if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
+          continue;
+      
+        String argS = argDisplayString(a);
+        if (argS.length() > maxArgLength)
+          maxArgLength = argS.length();
+      }
+      */
+      int maxArgLength = DESCRIPTIONINDENT;
+
+      // always show --help
+      appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
+      sb.append(System.lineSeparator());
+
+      if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
       {
-        descLines = Arrays.stream(a.getDescription().split("\\n"))
-                .iterator();
+        appendUsageGeneral(sb, maxArgLength);
       }
-      sb.append(String.format("%-" + maxArgLength + "s", argSb.toString()));
-      boolean first = true;
-      if (descLines != null)
+
+      Iterator<Arg> argsI = args.iterator();
+      Type typeSection = null;
+      while (argsI.hasNext())
       {
-        while (descLines.hasNext())
+        Arg a = argsI.next();
+
+        if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
+                || a == Arg.HELP)
+        {
+          continue;
+        }
+
+        if (a.getType() != typeSection)
+        {
+          typeSection = a.getType();
+          String typeDescription = a.getType().description();
+          if (typeDescription != null && typeDescription.length() > 0)
+          {
+            // typeDescription = typeDescription.substring(0,
+            // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
+            typeDescription = typeDescription.toUpperCase(Locale.ROOT);
+            sb.append(typeDescription);
+            sb.append(System.lineSeparator());
+            sb.append(System.lineSeparator());
+          }
+        }
+
+        appendArgUsage(sb, a, maxArgLength);
+
+        if (argsI.hasNext())
         {
-          if (first)
-            sb.append(" - ");
-          else
-            sb.append(" ".repeat(maxArgLength + 3));
-          sb.append(descLines.next());
           sb.append(System.lineSeparator());
-          first = false;
         }
       }
+    }
+    return sb.toString();
+  }
 
-      List<String> options = new ArrayList<>();
+  private static void appendArgUsage(StringBuilder sb, Arg a,
+          int maxArgLength)
+  {
+    boolean first = appendArgAndDescription(sb, null, null, a,
+            maxArgLength);
+    List<String> options = new ArrayList<>();
 
-      if (a.hasOption(Opt.BOOLEAN))
-      {
-        options.add("default " + (a.getDefaultBoolValue() ? a.argString()
-                : a.negateArgString()));
-      }
+    if (a.hasOption(Opt.BOOLEAN))
+    {
+      options.add("default " + (a.getDefaultBoolValue() ? a.argString()
+              : a.negateArgString()));
+    }
 
-      if (a.hasOption(Opt.MULTI))
-      {
-        options.add("multiple");
-      }
+    if (a.hasOption(Opt.MULTI))
+    {
+      options.add("multiple");
+    }
 
-      if (a.hasOption(Opt.LINKED))
-      {
-        options.add("can be linked");
-      }
+    if (a.hasOption(Opt.LINKED))
+    {
+      options.add("can be linked");
+    }
 
-      if (a.hasOption(Opt.GLOB))
+    if (a.hasOption(Opt.GLOB))
+    {
+      options.add("allows file globs");
+    }
+
+    if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
+    {
+      options.add("allows substitutions");
+    }
+
+    if (a.hasOption(Opt.ALLOWALL))
+    {
+      options.add("can be applied to all linked arguments");
+    }
+
+    if (a.hasOption(Opt.PRIVATE))
+    {
+      options.add("for internal use only");
+    }
+
+    if (a.hasOption(Opt.SECRET))
+    {
+      options.add("for development use only");
+    }
+
+    if (options.size() > 0)
+    {
+      if (first)
       {
-        options.add("allows file globs");
+        sb.append(ARGDESCRIPTIONSEPARATOR);
       }
-
-      if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
+      else
       {
-        options.add("allows substitutions");
+        sb.append(String.format("%-"
+                + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
+                ""));
       }
+      sb.append("(");
+      sb.append(String.join("; ", options));
+      sb.append(')');
+      sb.append(System.lineSeparator());
+    }
+  }
+
+  public static String argDisplayString(Arg a)
+  {
+    StringBuilder argSb = new StringBuilder();
+    argSb.append(
+            a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
+    if (a.hasOption(Opt.STRING))
+      argSb.append("=value");
+    return argSb.toString();
+  }
+
+  public static boolean appendArgAndDescription(StringBuilder sb,
+          String aString, String description, Arg a, int maxArgLength)
+  {
+    return appendArgAndDescription(sb, aString, description, a,
+            maxArgLength, Platform.consoleWidth());
+  }
+
+  public static boolean appendArgAndDescription(StringBuilder sb,
+          String aString, String description, Arg a, int maxArgLength,
+          int maxLength)
+  {
+    if (aString == null && a != null)
+    {
+      aString = argDisplayString(a);
+    }
+    if (description == null && a != null)
+    {
+      description = a.getDescription();
+    }
+    sb.append(String.format("%-" + maxArgLength + "s", aString));
+    if (aString.length() > maxArgLength)
+    {
+      sb.append(System.lineSeparator());
+      sb.append(String.format("%-" + maxArgLength + "s", ""));
+    }
 
-      if (a.hasOption(Opt.ALLOWALL))
+    int descLength = maxLength - maxArgLength
+            - ARGDESCRIPTIONSEPARATOR.length();
+    // reformat the descriptions lines to the right width
+    Iterator<String> descLines = null;
+    if (description != null)
+    {
+      descLines = Arrays.stream(description.split("\\n")).iterator();
+    }
+    List<String> splitDescLinesList = new ArrayList<>();
+    while (descLines != null && descLines.hasNext())
+    {
+      String line = descLines.next();
+      while (line.length() > descLength)
       {
-        options.add("can be applied to all linked arguments");
+        int splitIndex = line.lastIndexOf(" ", descLength);
+        splitDescLinesList.add(line.substring(0, splitIndex));
+        line = line.substring(splitIndex + 1);
       }
+      splitDescLinesList.add(line);
+    }
 
-      if (a.hasOption(Opt.PRIVATE))
+    Iterator<String> splitDescLines = splitDescLinesList.iterator();
+    boolean first = true;
+    if (splitDescLines != null)
+    {
+      while (splitDescLines.hasNext())
       {
-        options.add("for internal use only");
+        if (first)
+        {
+          sb.append(ARGDESCRIPTIONSEPARATOR);
+        }
+        else
+        {
+          sb.append(String.format("%-"
+                  + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
+                  ""));
+        }
+        sb.append(splitDescLines.next());
+        sb.append(System.lineSeparator());
+        first = false;
       }
+    }
+    return first;
+  }
+
+  protected static Iterator<Arg> getAllOfType(Type type)
+  {
+    return getAllOfType(type, new Opt[] {});
+  }
 
-      if (a.hasOption(Opt.SECRET))
+  protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
+  {
+    Opt[] opts = options == null ? new Opt[] {} : options;
+    return EnumSet.allOf(Arg.class).stream().filter(a -> {
+      if (a.getType() != type)
+        return false;
+      for (Opt o : opts)
       {
-        options.add("for development use only");
+        if (!a.hasOption(o))
+          return false;
       }
+      return true;
+    }).iterator();
+  }
 
-      if (options.size() > 0)
+  private static List<Arg> argsSortedForDisplay(List<Type> types)
+  {
+    List<Arg> argsToSort;
+    // if no types provided, do all
+    if (types == null || types.size() == 0 || types.contains(Type.ALL))
+    {
+      argsToSort = Arrays
+              .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
+    }
+    else
+    {
+      argsToSort = new ArrayList<>();
+      for (Type type : types)
       {
-        if (first)
-          sb.append(" - ");
-        else
-          sb.append(" ".repeat(maxArgLength + 3));
-        sb.append("(");
-        sb.append(String.join("; ", options));
-        sb.append(')');
-        sb.append(System.lineSeparator());
+        if (type == null)
+          continue;
+        Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
       }
-      sb.append(System.lineSeparator());
     }
-    return sb.toString();
+
+    Collections.sort(argsToSort, new ArgDisplayComparator());
+    return argsToSort;
+  }
+
+  private static final String ARGDESCRIPTIONSEPARATOR = " - ";
+
+  private static final int DESCRIPTIONINDENT = 20;
+
+}
+
+class ArgDisplayComparator implements Comparator<Arg>
+{
+  private int compareArgOpts(Arg a, Arg b, Opt o)
+  {
+    int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
+            : (b.hasOption(o) ? 1 : 0);
+    return i;
+  }
+
+  private int compareForDisplay(Arg a, Arg b)
+  {
+    if (b == null)
+      return -1;
+    // first compare types (in enum order)
+    int i = a.getType().compareTo(b.getType());
+    if (i != 0)
+      return i;
+    // do Opt.LAST next (oddly). Reversed args important!
+    i = compareArgOpts(b, a, Opt.LAST);
+    if (i != 0)
+      return i;
+    // priority order
+    Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
+        Opt.BOOLEAN };
+    for (Opt o : optOrder)
+    {
+      i = compareArgOpts(a, b, o);
+      if (i != 0)
+        return i;
+    }
+    // finally order of appearance in enum declarations
+    return a.compareTo(b);
+  }
+
+  @Override
+  public int compare(Arg a, Arg b)
+  {
+    return compareForDisplay(a, b);
   }
 }
\ No newline at end of file
index e6bd917..6d1251c 100644 (file)
@@ -38,11 +38,14 @@ import jalview.bin.Cache;
 import jalview.bin.Console;
 import jalview.bin.Jalview;
 import jalview.bin.argparser.Arg.Opt;
+import jalview.bin.argparser.Arg.Type;
 import jalview.util.FileUtils;
 import jalview.util.HttpUtils;
 
 public class ArgParser
 {
+  protected static final String SINGLEDASH = "-";
+
   protected static final String DOUBLEDASH = "--";
 
   protected static final char EQUALS = '=';
@@ -105,10 +108,6 @@ public class ArgParser
   // --argfile name
   private static final String ARGFILEDIRNAME = "{argfiledirname}";
 
-  // an output file wildcard to signify --output=*.ext is really --all --output
-  // {basename}.ext
-  private static final String OUTPUTWILDCARD = "*.";
-
   // flag to say whether {n} subtitutions in output filenames should be made.
   // Turn on and off with --substitutions and --nosubstitutions
   // Start with it on
@@ -148,8 +147,7 @@ public class ArgParser
         if (argMap.containsKey(argName))
         {
           Console.warn("Trying to add argument name multiple times: '"
-                  + argName + "'"); // RESTORE THIS WHEN
-          // MERGED
+                  + argName + "'");
           if (argMap.get(argName) != a)
           {
             Console.error(
@@ -246,13 +244,17 @@ public class ArgParser
         openEachInitialFilenames = false;
       }
 
-      String argName = null;
-      String val = null;
-      List<String> globVals = null; // for Opt.GLOB only
-      SubVals globSubVals = null; // also for use by Opt.GLOB only
-      String linkedId = null;
+      // look for double-dash, e.g. --arg
       if (arg.startsWith(DOUBLEDASH))
       {
+        String argName = null;
+        String val = null;
+        List<String> globVals = null; // for Opt.GLOB only
+        SubVals globSubVals = null; // also for use by Opt.GLOB only
+        String linkedId = null;
+        Type type = null;
+
+        // look for equals e.g. --arg=value
         int equalPos = arg.indexOf(EQUALS);
         if (equalPos > -1)
         {
@@ -263,17 +265,38 @@ public class ArgParser
         {
           argName = arg.substring(DOUBLEDASH.length());
         }
+
+        // look for linked ID e.g. --arg[linkedID]
         int idOpen = argName.indexOf('[');
         int idClose = argName.indexOf(']');
-
         if (idOpen > -1 && idClose == argName.length() - 1)
         {
           linkedId = argName.substring(idOpen + 1, idClose);
           argName = argName.substring(0, idOpen);
         }
 
+        // look for type modification e.g. --help-opening
+        int dashPos = argName.indexOf(SINGLEDASH);
+        if (dashPos > -1)
+        {
+          String potentialArgName = argName.substring(0, dashPos);
+          Arg potentialArg = argMap.get(potentialArgName);
+          if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
+          {
+            String typeName = argName.substring(dashPos + 1);
+            try
+            {
+              type = Type.valueOf(typeName);
+            } catch (IllegalArgumentException e)
+            {
+              type = Type.INVALID;
+            }
+            argName = argName.substring(0, dashPos);
+          }
+        }
+
         Arg a = argMap.get(argName);
-        // check for boolean prepended by "no"
+        // check for boolean prepended by "no" e.g. --nowrap
         boolean negated = false;
         if (a == null && argName.startsWith(NEGATESTRING) && argMap
                 .containsKey(argName.substring(NEGATESTRING.length())))
@@ -416,7 +439,6 @@ public class ArgParser
         }
 
         String autoCounterString = null;
-        boolean usingAutoCounterLinkedId = false;
         String defaultLinkedId = defaultLinkedId(false);
         boolean usingDefaultLinkedId = false;
         if (a.hasOption(Opt.LINKED))
@@ -469,7 +491,6 @@ public class ArgParser
             autoCounterString = Integer.toString(linkedIdAutoCounter);
             linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
                     autoCounterString);
-            usingAutoCounterLinkedId = true;
             Console.debug(
                     "Changing linkedId to '" + linkedId + "' from " + arg);
           }
@@ -479,7 +500,6 @@ public class ArgParser
             autoCounterString = Integer.toString(++linkedIdAutoCounter);
             linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
                     autoCounterString);
-            usingAutoCounterLinkedId = true;
             Console.debug(
                     "Changing linkedId to '" + linkedId + "' from " + arg);
           }
@@ -527,7 +547,7 @@ public class ArgParser
             {
               String v = gve.nextElement();
               SubVals vsv = new SubVals(globSubVals, v);
-              addValue(linkedId, avs, vsv, v, argIndex++, true);
+              addValue(linkedId, type, avs, vsv, v, argIndex++, true);
               // if we're using defaultLinkedId and the arg increments the
               // counter:
               if (gve.hasMoreElements() && usingDefaultLinkedId
@@ -543,17 +563,17 @@ public class ArgParser
           }
           else
           {
-            addValue(linkedId, avs, val, argIndex, true);
+            addValue(linkedId, type, avs, val, argIndex, true);
           }
         }
         else if (a.hasOption(Opt.BOOLEAN))
         {
-          setBoolean(linkedId, avs, !negated, argIndex);
+          setBoolean(linkedId, type, avs, !negated, argIndex);
           setNegated(linkedId, avs, negated);
         }
         else if (a.hasOption(Opt.UNARY))
         {
-          setBoolean(linkedId, avs, true, argIndex);
+          setBoolean(linkedId, type, avs, true, argIndex);
         }
 
         // remove the '*' or 'open*' linkedId that should be empty if it was
@@ -981,37 +1001,37 @@ public class ArgParser
   // the following methods look for the "*" linkedId and add the argvalue to all
   // linkedId ArgValues if it does.
   // This version inserts the subvals sv into all created values
-  private void addValue(String linkedId, ArgValues avs, SubVals sv,
-          String v, int argIndex, boolean doSubs)
+  private void addValue(String linkedId, Type type, ArgValues avs,
+          SubVals sv, String v, int argIndex, boolean doSubs)
   {
-    this.argValueOperation(Op.ADDVALUE, linkedId, avs, sv, v, false,
+    this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, sv, v, false,
             argIndex, doSubs);
   }
 
-  private void addValue(String linkedId, ArgValues avs, String v,
+  private void addValue(String linkedId, Type type, ArgValues avs, String v,
           int argIndex, boolean doSubs)
   {
-    this.argValueOperation(Op.ADDVALUE, linkedId, avs, null, v, false,
+    this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, null, v, false,
             argIndex, doSubs);
   }
 
-  private void setBoolean(String linkedId, ArgValues avs, boolean b,
-          int argIndex)
+  private void setBoolean(String linkedId, Type type, ArgValues avs,
+          boolean b, int argIndex)
   {
-    this.argValueOperation(Op.SETBOOLEAN, linkedId, avs, null, null, b,
-            argIndex, false);
+    this.argValueOperation(Op.SETBOOLEAN, linkedId, type, avs, null, null,
+            b, argIndex, false);
   }
 
   private void setNegated(String linkedId, ArgValues avs, boolean b)
   {
-    this.argValueOperation(Op.SETNEGATED, linkedId, avs, null, null, b, 0,
-            false);
+    this.argValueOperation(Op.SETNEGATED, linkedId, null, avs, null, null,
+            b, 0, false);
   }
 
   private void incrementCount(String linkedId, ArgValues avs)
   {
-    this.argValueOperation(Op.INCREMENTCOUNT, linkedId, avs, null, null,
-            false, 0, false);
+    this.argValueOperation(Op.INCREMENTCOUNT, linkedId, null, avs, null,
+            null, false, 0, false);
   }
 
   private enum Op
@@ -1022,8 +1042,9 @@ public class ArgParser
   // The following operations look for the "*" and "open*" linkedIds and add the
   // argvalue to all appropriate linkedId ArgValues if it does.
   // If subvals are supplied, they are inserted into all new set values.
-  private void argValueOperation(Op op, String linkedId, ArgValues avs,
-          SubVals sv, String v, boolean b, int argIndex, boolean doSubs)
+  private void argValueOperation(Op op, String linkedId, Type type,
+          ArgValues avs, SubVals sv, String v, boolean b, int argIndex,
+          boolean doSubs)
   {
     Arg a = avs.arg();
 
@@ -1079,7 +1100,7 @@ public class ArgParser
               val = makeSubstitutions(v, id);
               sv = new SubVals(sv, val);
             }
-            tavs.addValue(sv, val, argIndex, true);
+            tavs.addValue(sv, type, val, argIndex, true);
           }
           else
           {
@@ -1087,13 +1108,13 @@ public class ArgParser
             {
               val = makeSubstitutions(v, id);
             }
-            tavs.addValue(val, argIndex, true);
+            tavs.addValue(type, val, argIndex, true);
           }
           finaliseStoringArgValue(id, tavs);
           break;
 
         case SETBOOLEAN:
-          tavs.setBoolean(b, argIndex, true);
+          tavs.setBoolean(type, b, argIndex, true);
           finaliseStoringArgValue(id, tavs);
           break;
 
@@ -1125,7 +1146,7 @@ public class ArgParser
             val = makeSubstitutions(v, linkedId);
             sv = new SubVals(sv, val);
           }
-          avs.addValue(sv, val, argIndex, false);
+          avs.addValue(sv, type, val, argIndex, false);
         }
         else
         {
@@ -1133,13 +1154,13 @@ public class ArgParser
           {
             val = makeSubstitutions(v, linkedId);
           }
-          avs.addValue(val, argIndex, false);
+          avs.addValue(type, val, argIndex, false);
         }
         finaliseStoringArgValue(linkedId, avs);
         break;
 
       case SETBOOLEAN:
-        avs.setBoolean(b, argIndex, false);
+        avs.setBoolean(type, b, argIndex, false);
         finaliseStoringArgValue(linkedId, avs);
         break;
 
index 1643713..f76b757 100644 (file)
@@ -1,5 +1,8 @@
 package jalview.bin.argparser;
 
+import jalview.bin.argparser.Arg.Opt;
+import jalview.bin.argparser.Arg.Type;
+
 /**
  * A helper class to keep an index of argument position with argument values
  */
@@ -11,26 +14,45 @@ public class ArgValue implements Comparable<ArgValue>
 
   private String value;
 
+  /*
+   * Type type is only really used by --help-type
+   */
+  private Type type = null;
+
   // This id is set by a subVal id= to identify the product of this ArgValue
   // later. Set but not currently used.
   private String id;
 
   private SubVals subVals;
 
-  protected ArgValue(Arg a, SubVals sv, String content, int argIndex)
+  protected ArgValue(Arg a, SubVals sv, Type type, String content,
+          int argIndex)
   {
     this.arg = a;
     this.value = content;
     this.argIndex = argIndex;
     this.subVals = sv == null ? new SubVals("") : sv;
+    this.setType(type);
   }
 
-  protected ArgValue(Arg a, String value, int argIndex)
+  protected ArgValue(Arg a, Type type, String value, int argIndex)
   {
     this.arg = a;
     this.argIndex = argIndex;
     this.subVals = new SubVals(value);
     this.value = getSubVals().getContent();
+    this.setType(type);
+  }
+
+  protected void setType(Type t)
+  {
+    if (this.getArg().hasOption(Opt.HASTYPE))
+      this.type = t;
+  }
+
+  public Type getType()
+  {
+    return type;
   }
 
   public Arg getArg()
@@ -76,7 +98,7 @@ public class ArgValue implements Comparable<ArgValue>
   }
 
   @Override
-  public int compareTo(ArgValue o)
+  public final int compareTo(ArgValue o)
   {
     return this.getArgIndex() - o.getArgIndex();
   }
index f2c299c..f25fc9a 100644 (file)
@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
 
 import jalview.bin.Console;
 import jalview.bin.argparser.Arg.Opt;
+import jalview.bin.argparser.Arg.Type;
 
 public class ArgValues
 {
@@ -31,6 +32,11 @@ public class ArgValues
 
   private Map<String, ArgValue> idMap = new HashMap<>();
 
+  /*
+   * Type type is only really used by --help-type
+   */
+  private Type type = null;
+
   protected ArgValues(Arg a)
   {
     this.arg = a;
@@ -53,6 +59,17 @@ public class ArgValues
     return arg;
   }
 
+  protected void setType(Type t)
+  {
+    if (this.arg().hasOption(Opt.HASTYPE))
+      this.type = t;
+  }
+
+  public Type getType()
+  {
+    return type;
+  }
+
   protected int getCount()
   {
     return argCount;
@@ -76,8 +93,10 @@ public class ArgValues
     return this.negated;
   }
 
-  protected void setBoolean(boolean b, int i, boolean beingSetByWildcard)
+  protected void setBoolean(Type t, boolean b, int i,
+          boolean beingSetByWildcard)
   {
+    this.setType(t);
     // don't overwrite a wildcard set boolean with a non-wildcard set boolean
     if (boolIndex >= 0 && !this.setByWildcard && beingSetByWildcard)
       return;
@@ -117,15 +136,16 @@ public class ArgValues
     return sb.toString();
   }
 
-  protected void addValue(String val, int argIndex, boolean wildcard)
+  protected void addValue(Type type, String val, int argIndex,
+          boolean wildcard)
   {
-    addArgValue(new ArgValue(arg(), val, argIndex), wildcard);
+    addArgValue(new ArgValue(arg(), type, val, argIndex), wildcard);
   }
 
-  protected void addValue(SubVals sv, String content, int argIndex,
-          boolean wildcard)
+  protected void addValue(SubVals sv, Type type, String content,
+          int argIndex, boolean wildcard)
   {
-    addArgValue(new ArgValue(arg(), sv, content, argIndex), wildcard);
+    addArgValue(new ArgValue(arg(), sv, type, content, argIndex), wildcard);
   }
 
   protected void addArgValue(ArgValue av, boolean beingSetByWildcard)
index 14b7fe6..faa4a43 100644 (file)
@@ -1,21 +1,25 @@
 package jalview.bin.argparser;
 
 import java.io.File;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import jalview.bin.argparser.Arg.Opt;
+import jalview.bin.argparser.Arg.Type;
 import jalview.util.FileUtils;
 
 public class BootstrapArgs
 {
   // only need one
-  private Map<Arg, List<String>> bootstrapArgMap = new HashMap<>();
+  private Map<Arg, List<Map.Entry<Type, String>>> bootstrapArgMap = new HashMap<>();
 
   private Set<File> argFiles = new HashSet<>();
 
@@ -54,30 +58,54 @@ public class BootstrapArgs
     for (int i = 0; i < args.size(); i++)
     {
       String arg = args.get(i);
-      String argName = null;
-      String val = null;
+      // look for double-dash, e.g. --arg
       if (arg.startsWith(ArgParser.DOUBLEDASH))
       {
+        String argName = null;
+        String val = null;
+        Type type = null;
         // remove "--"
-        arg = arg.substring(ArgParser.DOUBLEDASH.length());
-        int equalPos = arg.indexOf(ArgParser.EQUALS);
-        if (equalPos > -1
-                && ArgParser.argMap.containsKey(arg.substring(0, equalPos)))
+        argName = arg.substring(ArgParser.DOUBLEDASH.length());
+
+        // look for equals e.g. --arg=value
+        int equalPos = argName.indexOf(ArgParser.EQUALS);
+        if (equalPos > -1)
         {
-          argName = arg.substring(0, equalPos);
-          val = arg.substring(equalPos + 1);
+          val = argName.substring(equalPos + 1);
+          argName = argName.substring(0, equalPos);
         }
+
         // check for boolean prepended by "no"
-        else if (arg.startsWith(ArgParser.NEGATESTRING)
+        if (argName.startsWith(ArgParser.NEGATESTRING)
                 && ArgParser.argMap.containsKey(
-                        arg.substring(ArgParser.NEGATESTRING.length())))
+                        argName.substring(ArgParser.NEGATESTRING.length())))
         {
-          argName = arg.substring(ArgParser.NEGATESTRING.length());
           val = "false";
+          argName = argName.substring(ArgParser.NEGATESTRING.length());
         }
-        else if (ArgParser.argMap.containsKey(arg))
+
+        // look for type modification e.g. --help-opening
+        int dashPos = argName.indexOf(ArgParser.SINGLEDASH);
+        if (dashPos > -1)
+        {
+          String potentialArgName = argName.substring(0, dashPos);
+          Arg potentialArg = ArgParser.argMap.get(potentialArgName);
+          if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
+          {
+            String typeName = argName.substring(dashPos + 1);
+            try
+            {
+              type = Type.valueOf(typeName.toUpperCase(Locale.ROOT));
+            } catch (IllegalArgumentException e)
+            {
+              type = Type.INVALID;
+            }
+            argName = argName.substring(0, dashPos);
+          }
+        }
+
+        if (ArgParser.argMap.containsKey(argName) && val == null)
         {
-          argName = arg;
           val = "true";
         }
 
@@ -108,7 +136,7 @@ public class BootstrapArgs
               vals.add(val);
             }
           }
-          addAll(a, vals);
+          addAll(a, type, vals);
 
           if (a == Arg.ARGFILE)
           {
@@ -121,7 +149,7 @@ public class BootstrapArgs
         }
         else
         {
-          add(a, val);
+          add(a, type, val);
         }
       }
     }
@@ -132,14 +160,55 @@ public class BootstrapArgs
     return bootstrapArgMap.containsKey(a);
   }
 
-  public List<String> getList(Arg a)
+  public boolean containsType(Type t)
+  {
+    for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values())
+    {
+      for (Map.Entry<Type, String> e : l)
+      {
+        if (e.getKey() == t)
+          return true;
+      }
+    }
+    return false;
+  }
+
+  public List<Arg> getArgsOfType(Type t)
+  {
+    return getArgsOfType(t, new Opt[] {});
+  }
+
+  public List<Arg> getArgsOfType(Type t, Opt... opts)
+  {
+    List<Arg> args = new ArrayList<>();
+    for (Arg a : bootstrapArgMap.keySet())
+    {
+      if (!a.hasAllOptions(opts))
+        continue;
+
+      List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a);
+      if (l.stream().anyMatch(e -> e.getKey() == t))
+      {
+        args.add(a);
+      }
+    }
+    return args;
+  }
+
+  public List<Map.Entry<Type, String>> getList(Arg a)
   {
     return bootstrapArgMap.get(a);
   }
 
-  private List<String> getOrCreateList(Arg a)
+  public List<String> getValueList(Arg a)
   {
-    List<String> l = getList(a);
+    return bootstrapArgMap.get(a).stream().map(e -> e.getValue())
+            .collect(Collectors.toList());
+  }
+
+  private List<Map.Entry<Type, String>> getOrCreateList(Arg a)
+  {
+    List<Map.Entry<Type, String>> l = getList(a);
     if (l == null)
     {
       l = new ArrayList<>();
@@ -148,7 +217,7 @@ public class BootstrapArgs
     return l;
   }
 
-  private void putList(Arg a, List<String> l)
+  private void putList(Arg a, List<Map.Entry<Type, String>> l)
   {
     bootstrapArgMap.put(a, l);
   }
@@ -159,28 +228,36 @@ public class BootstrapArgs
    * and the arg is not MULTI (so first expressed value is
    * retained).
    */
-  private void add(Arg a, String s)
+  private void add(Arg a, Type t, String s)
   {
-    List<String> l = getOrCreateList(a);
+    List<Map.Entry<Type, String>> l = getOrCreateList(a);
     if (a.hasOption(Opt.MULTI) || l.size() == 0)
     {
-      l.add(s);
+      l.add(entry(t, s));
     }
   }
 
-  private void addAll(Arg a, List<String> al)
+  private void addAll(Arg a, Type t, List<String> al)
   {
-    List<String> l = getOrCreateList(a);
+    List<Map.Entry<Type, String>> l = getOrCreateList(a);
     if (a.hasOption(Opt.MULTI))
     {
-      l.addAll(al);
+      for (String s : al)
+      {
+        l.add(entry(t, s));
+      }
     }
     else if (l.size() == 0 && al.size() > 0)
     {
-      l.add(al.get(0));
+      l.add(entry(t, al.get(0)));
     }
   }
 
+  private static Map.Entry<Type, String> entry(Type t, String s)
+  {
+    return new AbstractMap.SimpleEntry<Type, String>(t, s);
+  }
+
   /*
    * Retrieves the first value even if MULTI.
    * A convenience for non-MULTI args.
@@ -189,8 +266,8 @@ public class BootstrapArgs
   {
     if (!bootstrapArgMap.containsKey(a))
       return null;
-    List<String> aL = bootstrapArgMap.get(a);
-    return (aL == null || aL.size() == 0) ? null : aL.get(0);
+    List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a);
+    return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue();
   }
 
   public boolean getBoolean(Arg a, boolean d)
index d52ff89..dd7fd94 100644 (file)
@@ -696,6 +696,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI
     this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
     this.add(pnl_results, java.awt.BorderLayout.CENTER);
     this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
+    mainFrame.setFrameIcon(null);
     mainFrame.setVisible(true);
     if (tabs != null)
     {
index 5b886b6..f6ab8c9 100755 (executable)
@@ -263,6 +263,7 @@ public class GAlignFrame extends JInternalFrame
 
   private void jbInit() throws Exception
   {
+    setFrameIcon(null);
     initColourMenu();
 
     JMenuItem saveAs = new JMenuItem(
index ad1c1cf..ef09b6a 100644 (file)
@@ -20,9 +20,6 @@
  */
 package jalview.jbgui;
 
-import jalview.gui.JvSwingUtils;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
@@ -41,6 +38,9 @@ import javax.swing.JScrollPane;
 import javax.swing.text.EditorKit;
 import javax.swing.text.html.HTMLEditorKit;
 
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+
 /**
  * DOCUMENT ME!
  * 
@@ -103,6 +103,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
    */
   private void jbInit() throws Exception
   {
+    setFrameIcon(null);
     scrollPane.setBorder(null);
     ok.setFont(JvSwingUtils.getLabelFont());
     ok.setText(MessageManager.getString("label.new_window"));
index b75dd8c..3cd52ae 100755 (executable)
  */
 package jalview.jbgui;
 
-import jalview.gui.JvSwingUtils;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Font;
-import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
@@ -39,6 +35,9 @@ import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTextArea;
 
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+
 /**
  * DOCUMENT ME!
  * 
@@ -98,6 +97,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
    */
   private void jbInit() throws Exception
   {
+    setFrameIcon(null);
     scrollPane.setBorder(null);
     ok.setFont(JvSwingUtils.getLabelFont());
     ok.setText(MessageManager.getString("label.new_window"));
index a6498d2..cf7bc4e 100755 (executable)
@@ -20,9 +20,6 @@
  */
 package jalview.jbgui;
 
-import jalview.util.ImageMaker.TYPE;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.FlowLayout;
@@ -43,6 +40,9 @@ import javax.swing.JPanel;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
+import jalview.util.ImageMaker.TYPE;
+import jalview.util.MessageManager;
+
 public class GPCAPanel extends JInternalFrame
 {
   private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
@@ -88,6 +88,7 @@ public class GPCAPanel extends JInternalFrame
 
   private void jbInit() throws Exception
   {
+    setFrameIcon(null);
     setName("jalview-pca");
     this.getContentPane().setLayout(new BorderLayout());
     JPanel jPanel2 = new JPanel();
index 5dc726f..663245f 100644 (file)
@@ -38,6 +38,7 @@ public class GRnaStructureViewer extends JInternalFrame
 
   private void jbInit() throws Exception
   {
+    setFrameIcon(null);
 
     setName("jalview-rnastructureviewer");
 
index 5e65047..4d531be 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.jbgui;
 
-import jalview.util.Platform;
-
 import java.awt.Component;
 import java.awt.MouseInfo;
 import java.awt.Point;
@@ -31,6 +29,8 @@ import javax.swing.JInternalFrame;
 import javax.swing.JSplitPane;
 import javax.swing.plaf.basic.BasicInternalFrameUI;
 
+import jalview.util.Platform;
+
 public class GSplitFrame extends JInternalFrame
 {
   protected static final int DIVIDER_SIZE = 5;
@@ -57,6 +57,7 @@ public class GSplitFrame extends JInternalFrame
    */
   public GSplitFrame(GAlignFrame top, GAlignFrame bottom)
   {
+    setFrameIcon(null);
     setName("jalview-splitframe");
     this.topFrame = top;
     this.bottomFrame = bottom;
index 4e13032..18e4c9f 100644 (file)
@@ -88,7 +88,7 @@ public abstract class GStructureViewer extends JInternalFrame
 
   private void jbInit() throws Exception
   {
-
+    setFrameIcon(null);
     setName("jalview-structureviewer");
 
     JMenuBar menuBar = new JMenuBar();
index d184e76..2a1b11f 100755 (executable)
@@ -20,9 +20,6 @@
  */
 package jalview.jbgui;
 
-import jalview.util.ImageMaker.TYPE;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.event.ActionEvent;
@@ -37,6 +34,9 @@ import javax.swing.JScrollPane;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
+import jalview.util.ImageMaker.TYPE;
+import jalview.util.MessageManager;
+
 @SuppressWarnings("serial")
 public class GTreePanel extends JInternalFrame
 {
@@ -92,6 +92,7 @@ public class GTreePanel extends JInternalFrame
 
   private void jbInit() throws Exception
   {
+    setFrameIcon(null);
     setName("jalview-tree");
     this.getContentPane().setLayout(borderLayout1);
     this.setBackground(Color.white);
index 573e2d5..f5953bf 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.util;
 
-import jalview.javascript.json.JSON;
-
 import java.awt.Toolkit;
 import java.awt.event.MouseEvent;
 import java.io.BufferedReader;
@@ -40,6 +38,8 @@ import javax.swing.SwingUtilities;
 import org.json.simple.parser.JSONParser;
 import org.json.simple.parser.ParseException;
 
+import jalview.javascript.json.JSON;
+
 /**
  * System platform information used by Applet and Application
  * 
@@ -56,6 +56,27 @@ public class Platform
 
   private static Boolean isHeadless = null;
 
+  // If launched from CLI with launcher script then -DCOLUMNWIDTH is set
+  private static final int CONSOLEWIDTH;
+
+  private static final String CONSOLEWIDTHPROPERTY = "CONSOLEWIDTH";
+
+  static
+  {
+    int cw = 80;
+    if (System.getProperty(CONSOLEWIDTHPROPERTY) != null
+            && System.getProperty(CONSOLEWIDTHPROPERTY).length() > 0)
+    {
+      try
+      {
+        cw = Integer.parseInt(System.getProperty(CONSOLEWIDTHPROPERTY));
+      } catch (NumberFormatException e)
+      {
+      }
+    }
+    CONSOLEWIDTH = cw;
+  }
+
   /**
    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
    * 
@@ -657,4 +678,12 @@ public class Platform
     String p2 = path2.replace('\\', '/');
     return p1.equals(p2);
   }
+
+  /**
+   * If started on command line using launch script, return the console width
+   */
+  public static int consoleWidth()
+  {
+    return CONSOLEWIDTH;
+  }
 }
index efaf3e2..1c67c92 100644 (file)
@@ -617,6 +617,22 @@ public class StringUtils
     {
       return string;
     }
+
   }
 
+  /* 
+   * return the maximum length of a List of Strings
+   */
+  public static int maxLength(List<String> l)
+  {
+    int max = 0;
+    for (String s : l)
+    {
+      if (s == null)
+        continue;
+      if (s.length() > max)
+        max = s.length();
+    }
+    return max;
+  }
 }
index f056da2..bd63232 100644 (file)
@@ -26,7 +26,7 @@ public class CommandsTest2
   @BeforeClass(alwaysRun = true)
   public static void setUpBeforeClass() throws Exception
   {
-    Cache.loadProperties("test/jalview/bin/commandsTest2.jvprops");
+    Cache.loadProperties("test/jalview/bin/commandsTest.jvprops");
     Date oneHourFromNow = new Date(
             System.currentTimeMillis() + 3600 * 1000);
     Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED", oneHourFromNow);
index 259acac..bc2be78 100644 (file)
@@ -97,7 +97,7 @@ public class ArgParserTest
     }
     else if (a == Arg.ARGFILE)
     {
-      List<String> filenames = b.getList(a);
+      List<String> filenames = b.getValueList(a);
       boolean found = false;
       for (String s : filenames)
       {
index 5be7ab9..62816e5 100644 (file)
@@ -1,22 +1,22 @@
-application/x-jalview+xml+zip; jalview -open '%s'; description="Jalview File"; nametemplate=%s.jvp; test=test -n "$DISPLAY"; priority=10
-chemical/x-cif; jalview -open '%s'; description="CIF File"; nametemplate=%s.cif; test=test -n "$DISPLAY"; priority=4
-chemical/x-mmcif; jalview -open '%s'; description="mmCIF File"; nametemplate=%s.mcif; test=test -n "$DISPLAY"; priority=4
-chemical/x-pdb; jalview -open '%s'; description="PDB File"; nametemplate=%s.pdb; test=test -n "$DISPLAY"; priority=4
-application/x-amsa+txt; jalview -open '%s'; description="AMSA File"; nametemplate=%s.amsa; test=test -n "$DISPLAY"; priority=9
-application/x-jalview-annotations+text; jalview -open '%s'; description="Jalview Annotations File"; nametemplate=%s.annotations; test=test -n "$DISPLAY"; priority=10
-application/x-jalview-biojson+json; jalview -open '%s'; description="BioJSON File"; nametemplate=%s.biojson; test=test -n "$DISPLAY"; priority=10
-application/x-blc+txt; jalview -open '%s'; description="BLC File"; nametemplate=%s.blc; test=test -n "$DISPLAY"; priority=9
-application/x-clustal+txt; jalview -open '%s'; description="Clustal File"; nametemplate=%s.aln; test=test -n "$DISPLAY"; priority=9
-application/x-fasta+txt; jalview -open '%s'; description="Fasta File"; nametemplate=%s.fa; test=test -n "$DISPLAY"; priority=9
-application/x-jalview-features+text; jalview -open '%s'; description="Jalview Features File"; nametemplate=%s.features; test=test -n "$DISPLAY"; priority=10
-application/x-gff2+txt; jalview -open '%s'; description="Generic Features Format v2 File"; nametemplate=%s.gff2; test=test -n "$DISPLAY"; priority=9
-application/x-gff3+txt; jalview -open '%s'; description="Generic Features Format v3 File"; nametemplate=%s.gff3; test=test -n "$DISPLAY"; priority=9
-application/x-jalview-jnet+text; jalview -open '%s'; description="JnetFile File"; nametemplate=%s.concise; test=test -n "$DISPLAY"; priority=10
-application/x-msf+txt; jalview -open '%s'; description="MSF File"; nametemplate=%s.msf; test=test -n "$DISPLAY"; priority=9
-application/x-pfam+txt; jalview -open '%s'; description="PFAM File"; nametemplate=%s.pfam; test=test -n "$DISPLAY"; priority=9
-application/x-phylip+txt; jalview -open '%s'; description="PHYLIP File"; nametemplate=%s.phy; test=test -n "$DISPLAY"; priority=9
-application/x-pileup+txt; jalview -open '%s'; description="PileUp File"; nametemplate=%s.pileup; test=test -n "$DISPLAY"; priority=9
-application/x-pir+txt; jalview -open '%s'; description="PIR File"; nametemplate=%s.pir; test=test -n "$DISPLAY"; priority=9
-application/rnaml+xml; jalview -open '%s'; description="RNAML File"; nametemplate=%s.rnaml; test=test -n "$DISPLAY"; priority=9
-application/x-jalview-scorematrix+text; jalview -open '%s'; description="Substitution Matrix File"; nametemplate=%s.mat; test=test -n "$DISPLAY"; priority=10
-application/x-stockholm+txt; jalview -open '%s'; description="Stockholm File"; nametemplate=%s.sto; test=test -n "$DISPLAY"; priority=9
+application/x-jalview+xml+zip; jalview '%s'; description="Jalview File"; nametemplate=%s.jvp; test=test -n "$DISPLAY"; priority=10
+chemical/x-cif; jalview '%s'; description="CIF File"; nametemplate=%s.cif; test=test -n "$DISPLAY"; priority=4
+chemical/x-mmcif; jalview '%s'; description="mmCIF File"; nametemplate=%s.mcif; test=test -n "$DISPLAY"; priority=4
+chemical/x-pdb; jalview '%s'; description="PDB File"; nametemplate=%s.pdb; test=test -n "$DISPLAY"; priority=4
+application/x-amsa+txt; jalview '%s'; description="AMSA File"; nametemplate=%s.amsa; test=test -n "$DISPLAY"; priority=9
+application/x-jalview-annotations+text; jalview '%s'; description="Jalview Annotations File"; nametemplate=%s.annotations; test=test -n "$DISPLAY"; priority=10
+application/x-jalview-biojson+json; jalview '%s'; description="BioJSON File"; nametemplate=%s.biojson; test=test -n "$DISPLAY"; priority=10
+application/x-blc+txt; jalview '%s'; description="BLC File"; nametemplate=%s.blc; test=test -n "$DISPLAY"; priority=9
+application/x-clustal+txt; jalview '%s'; description="Clustal File"; nametemplate=%s.aln; test=test -n "$DISPLAY"; priority=9
+application/x-fasta+txt; jalview '%s'; description="Fasta File"; nametemplate=%s.fa; test=test -n "$DISPLAY"; priority=9
+application/x-jalview-features+text; jalview '%s'; description="Jalview Features File"; nametemplate=%s.features; test=test -n "$DISPLAY"; priority=10
+application/x-gff2+txt; jalview '%s'; description="Generic Features Format v2 File"; nametemplate=%s.gff2; test=test -n "$DISPLAY"; priority=9
+application/x-gff3+txt; jalview '%s'; description="Generic Features Format v3 File"; nametemplate=%s.gff3; test=test -n "$DISPLAY"; priority=9
+application/x-jalview-jnet+text; jalview '%s'; description="JnetFile File"; nametemplate=%s.concise; test=test -n "$DISPLAY"; priority=10
+application/x-msf+txt; jalview '%s'; description="MSF File"; nametemplate=%s.msf; test=test -n "$DISPLAY"; priority=9
+application/x-pfam+txt; jalview '%s'; description="PFAM File"; nametemplate=%s.pfam; test=test -n "$DISPLAY"; priority=9
+application/x-phylip+txt; jalview '%s'; description="PHYLIP File"; nametemplate=%s.phy; test=test -n "$DISPLAY"; priority=9
+application/x-pileup+txt; jalview '%s'; description="PileUp File"; nametemplate=%s.pileup; test=test -n "$DISPLAY"; priority=9
+application/x-pir+txt; jalview '%s'; description="PIR File"; nametemplate=%s.pir; test=test -n "$DISPLAY"; priority=9
+application/rnaml+xml; jalview '%s'; description="RNAML File"; nametemplate=%s.rnaml; test=test -n "$DISPLAY"; priority=9
+application/x-jalview-scorematrix+text; jalview '%s'; description="Substitution Matrix File"; nametemplate=%s.mat; test=test -n "$DISPLAY"; priority=10
+application/x-stockholm+txt; jalview '%s'; description="Stockholm File"; nametemplate=%s.sto; test=test -n "$DISPLAY"; priority=9
index 4823bee..0f44579 100755 (executable)
@@ -7,11 +7,4 @@ if [ -n "${HOME}" -a \! -e ${HOME}/.jalview_properties ]; then
   /bin/cp /etc/jalview_properties ${HOME}/.jalview_properties
 fi
 
-# check to see if $1 is set and is not start of other cli set args
-OPEN=""
-if [ -n "$ARG1" -a "$ARG1" = "${ARG1#-}" ]; then
-  # first argument exists and does not start with a "-"
-  OPEN="-open"
-fi
-  
-java -jar /usr/share/java/jalview.jar $OPEN "$@"
+java -jar /usr/share/java/jalview.jar "$@"
index ccb1d75..3c986a1 100755 (executable)
@@ -1,3 +1,11 @@
-#!/usr/bin/env sh
+#!/usr/bin/env bash
 
-java -cp "./bin/main:./j11lib/*:./resources:./help" jalview.bin.Launcher "${@}"
+if command -v tput 2>&1 >/dev/null; then
+  COLUMNS=$(tput cols) 2>/dev/null
+elif command -v stty 2>&1 >/dev/null; then
+  COLUMNS=$(stty size | cut -d" " -f2) 2>/dev/null
+elif command -v resize 2>&1 >/dev/null; then
+  COLUMNS=$(resize -u | grep COLUMNS= | sed -e 's/.*=//;s/;//') 2>/dev/null
+fi
+
+java "-DCONSOLEWIDTH=${COLUMNS}" -cp "./bin/main:./j11lib/*:./resources:./help" jalview.bin.Launcher "${@}"
diff --git a/utils/eclipse/launcher b/utils/eclipse/launcher
deleted file mode 100755 (executable)
index ccb1d75..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env sh
-
-java -cp "./bin/main:./j11lib/*:./resources:./help" jalview.bin.Launcher "${@}"
index 7ae9a57..65998bc 100755 (executable)
@@ -57,12 +57,6 @@ $CMDPATH = ( Get-Item $MyInvocation.MyCommand.Path )
 $SCRIPTPATH = Readlink-f -Link $CMDPATH
 $DIR = Split-Path -Path $SCRIPTPATH -Parent
 
-# set the "-open" parameter if myArg1 is non-zero-length, and not "open" or starts with a "-"
-$OPEN = ""
-if ( $myArg1.length -gt 0 -and ( -not $myArg1.StartsWith("-") ) -and $myArg1 -cne "open" ) {
-  $OPEN = "-open"
-}
-
 $APPDIR = If ( ( Split-Path -Path $DIR -Leaf ) -eq "bin" ) { Split-Path -Path $DIR -Parent } Else { $DIR }
 $JAVAEXE = If ( $myIsWindows ) { "java.exe" } Else { "java" }
 $JAVA = Join-Path -Path $APPDIR -ChildPath ( "jre/" + $( If ( $myIsMacOS ) { "Contents/Home/" } Else { "" } ) + "bin/${JAVAEXE}" )
@@ -81,7 +75,14 @@ if ( -not ( Test-Path -Path "${JAVA}" ) ) {
 
 $CLASSPATH = ( Select-String -Path "${GETDOWNTXT}" -AllMatches -Pattern "code\s*=\s*(.*)$" | foreach { Join-Path -Path $APPDIR -ChildPath $($_.Matches.Groups[1].Value ) } ) -join $( If ( $myIsWindows ) { ";" } Else { ":" } )
 
+# get console width
+$CONSOLEWIDTH = $Host.UI.RawUI.WindowSize.Width
+
 # quote the args and the command (in case of spaces) with escape chars (`) and precede with & to indicate command not string
-$myArgsString = '"' + $($myArgs -join '" "') + '"'
-Invoke-Expression -Command "& `"${JAVA}`" -cp `"${CLASSPATH}`" jalview.bin.Launcher ${OPEN} ${myArgsString}"
+if ( $myArgs.count -eq 0 ) {
+  Invoke-Expression -Command "& `"${JAVA}`" `"-DCONSOLEWIDTH=${CONSOLEWIDTH}`" -cp `"${CLASSPATH}`" jalview.bin.Launcher"
+} else {
+  $myArgsString = '"' + $($myArgs -join '" "') + '"'
+  Invoke-Expression -Command "& `"${JAVA}`" `"-DCONSOLEWIDTH=${CONSOLEWIDTH}`" -cp `"${CLASSPATH}`" jalview.bin.Launcher ${myArgsString}"
+}
 
index 112c3e6..62da28c 100755 (executable)
@@ -77,10 +77,9 @@ fi
 
 # WINDOWS ONLY (Cygwin or WSL)
 # change paths for Cygwin or Windows Subsystem for Linux (WSL)
-if [ "${ISMACOS}" != 1 ]; then # macos doesn't like uname -o, best to avoid
+if [ "${ISMACOS}" != 1 ]; then # older macos doesn't like uname -o, best to avoid
   if [ "$(uname -o)" = "Cygwin" ]; then
   # CYGWIN
-    echo "When using relative paths in args within Cygwin, please start with './' or '../'" >&2
     CLASSPATH=$(cygpath -pw "${CLASSPATH}")
     # now for some arg paths fun. only translating paths starting with './', '../', '/' or '~'
     ARGS=()
@@ -91,9 +90,8 @@ if [ "${ISMACOS}" != 1 ]; then # macos doesn't like uname -o, best to avoid
         ARGS=( "${ARGS[@]}" "${ARG}" )
       fi
     done
-  elif uname -r | grep Microsoft >/dev/null; then
+  elif uname -r | grep -i microsoft | grep -i wsl >/dev/null; then
   # WSL
-    echo "When using relative paths in args within WSL, please start with './' or '../'" >&2
     CLASSPATH=""
     for JARPATH in "${JARPATHS[@]}"; do
       [ -n "${CLASSPATH}" ] && CLASSPATH="${CLASSPATH};"
@@ -115,18 +113,20 @@ if [ "${ISMACOS}" != 1 ]; then # macos doesn't like uname -o, best to avoid
   fi
 fi
 
+# get console width -- three ways to try, just in case
+if command -v tput 2>&1 >/dev/null; then
+  COLUMNS=$(tput cols) 2>/dev/null
+elif command -v stty 2>&1 >/dev/null; then
+  COLUMNS=$(stty size | cut -d" " -f2) 2>/dev/null
+elif command -v resize 2>&1 >/dev/null; then
+  COLUMNS=$(resize -u | grep COLUMNS= | sed -e 's/.*=//;s/;//') 2>/dev/null
+fi
+JVMARGS=( "${JVMARGS[@]}" "-DCONSOLEWIDTH=${COLUMNS}" )
+
 # Is there a bundled Java?  If not just try one in the PATH (do need .exe in WSL)
 if [ \! -e "${JAVA}" ]; then
   JAVA=$SYSJAVA
   echo "Cannot find bundled java, using system ${JAVA} and hoping for the best!" >&2
 fi
 
-# check to see if $1 is set and is not start of other cli args
-OPEN=""
-if [ -n "${ARG1}" -a "${ARG1}" = "${ARG1#-}" -a "${ARG1}" != "open" ]; then
- # first argument exists and does not start with a "-" and is not "open"
- OPEN="-open"
-fi
-
-# don't quote $OPEN (don't want it accidentally mistaken as an empty string arg!)
-"${JAVA}" "${JVMARGS[@]}" -cp "${CLASSPATH}" jalview.bin.Launcher ${OPEN} "${ARGS[@]}"
+"${JAVA}" "${JVMARGS[@]}" -cp "${CLASSPATH}" jalview.bin.Launcher "${ARGS[@]}"