Merge branch 'merge/develop_and_rebase_squashed_merge_JAL-3628+JAL-3608+JAL-3609...
authorBen Soares <b.soares@dundee.ac.uk>
Tue, 8 Dec 2020 20:42:31 +0000 (20:42 +0000)
committerBen Soares <b.soares@dundee.ac.uk>
Tue, 8 Dec 2020 20:42:31 +0000 (20:42 +0000)
45 files changed:
build.gradle
getdown/lib/FJVL_VERSION
getdown/lib/JVL_VERSION
getdown/lib/getdown-core.jar
getdown/lib/getdown-launcher-local.jar
getdown/lib/getdown-launcher.jar
getdown/src/getdown/ant/pom.xml
getdown/src/getdown/core/pom.xml
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java
getdown/src/getdown/core/src/main/java/jalview/bin/GetMemory.java
getdown/src/getdown/core/src/main/java/jalview/bin/HiDPISetting.java
getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java
getdown/src/getdown/core/src/main/java/jalview/bin/ScreenInfo.java [new file with mode: 0644]
getdown/src/getdown/launcher/pom.xml
getdown/src/getdown/mvn_cmd
getdown/src/getdown/pom.xml
help/help/html/features/preferences.html
j11lib/getdown-core.jar
j8lib/getdown-core.jar
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/bin/Cache.java
src/jalview/bin/HiDPISetting.java
src/jalview/bin/Jalview.java
src/jalview/bin/Launcher.java
src/jalview/bin/MemorySetting.java
src/jalview/bin/ScreenInfo.java [new file with mode: 0644]
src/jalview/gui/AlignFrame.java
src/jalview/gui/Console.java
src/jalview/gui/Desktop.java
src/jalview/gui/Preferences.java
src/jalview/gui/WsPreferences.java
src/jalview/io/BackupFiles.java
src/jalview/io/BackupFilesPresetEntry.java
src/jalview/jbgui/GPreferences.java
src/jalview/project/Jalview2XML.java
src/jalview/util/HttpUtils.java
src/jalview/ws/sifts/SiftsClient.java
test/jalview/bin/HiDPISettingTest1.java [new file with mode: 0644]
test/jalview/bin/HiDPISettingTest2.java [new file with mode: 0644]
test/jalview/bin/hidpiTestProps.jvprops [new file with mode: 0644]
utils/testnglibs/byte-buddy-1.10.15.jar [new file with mode: 0644]
utils/testnglibs/byte-buddy-agent-1.10.15.jar [new file with mode: 0644]
utils/testnglibs/mockito-core-3.6.0.jar [new file with mode: 0644]
utils/testnglibs/objenesis-3.1.jar [new file with mode: 0644]

index 71dae0f..db1b533 100644 (file)
@@ -1311,6 +1311,11 @@ test {
     println("Setting Test LaF to '${testLaf}'")
     systemProperty "laf", testLaf
   }
+  def testHiDPIScale = project.findProperty("test_HiDPIScale")
+  if (testHiDPIScale != null) {
+    println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
+    systemProperty "sun.java2d.uiScale", testHiDPIScale
+  }
   sourceCompatibility = compile_source_compatibility
   targetCompatibility = compile_target_compatibility
   jvmArgs += additional_compiler_args
index 8228610..df225c8 100644 (file)
@@ -1 +1 @@
-1.8.3-1.2.8_FJVL
+1.8.3-1.2.10_FJVL
index 3e098de..6f6eed4 100644 (file)
@@ -1 +1 @@
-1.8.3-1.2.8_JVL
+1.8.3-1.2.10_JVL
index f800c5c..dadce6e 100644 (file)
Binary files a/getdown/lib/getdown-core.jar and b/getdown/lib/getdown-core.jar differ
index c0ed4a1..88036f9 100644 (file)
Binary files a/getdown/lib/getdown-launcher-local.jar and b/getdown/lib/getdown-launcher-local.jar differ
index 4fddd99..4e2a98c 100644 (file)
Binary files a/getdown/lib/getdown-launcher.jar and b/getdown/lib/getdown-launcher.jar differ
index 756bb16..9b26d50 100644 (file)
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.threerings.getdown</groupId>
     <artifactId>getdown</artifactId>
-    <version>1.8.3-1.2.8_FJVL</version>
+    <version>1.8.3-1.2.10_FJVL</version>
   </parent>
 
   <artifactId>getdown-ant</artifactId>
index 7c4d779..eb6f388 100644 (file)
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.threerings.getdown</groupId>
     <artifactId>getdown</artifactId>
-    <version>1.8.3-1.2.8_FJVL</version>
+    <version>1.8.3-1.2.10_FJVL</version>
   </parent>
 
   <artifactId>getdown-core</artifactId>
index b301a0f..2022750 100644 (file)
@@ -9,6 +9,8 @@ import java.io.*;
 import java.lang.reflect.Method;
 import java.net.MalformedURLException;
 import java.net.Proxy;
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -1058,13 +1060,30 @@ public class Application
         }
 
         // pass along our proxy settings
-        String proxyHost;
-        if ((proxyHost = System.getProperty("http.proxyHost")) != null) {
+        String proxyHost = System.getProperty("http.proxyHost");
+       String proxyPort = StringUtil.isBlank(System.getProperty("http.proxyPort")) ? "80" : System.getProperty("http.proxyPort");
+        if (StringUtil.isBlank(proxyHost) && ! proxy.equals(Proxy.NO_PROXY)) {
+           try {
+               SocketAddress a = proxy.address();
+               if (a != null && a instanceof InetSocketAddress) {
+                   InetSocketAddress ia = (InetSocketAddress)a;
+                   proxyHost = ia.getHostString();
+                   proxyPort = String.valueOf(ia.getPort());
+               }
+           } catch (Exception e) {
+               log.error("Problem obtaining proxy settings from Proxy object");
+               e.printStackTrace();
+           }
+       }
+        if (proxyHost != null) {
+           log.info("Using proxy settings "+proxyHost+":"+proxyPort);
             args.add("-Dhttp.proxyHost=" + proxyHost);
-            args.add("-Dhttp.proxyPort=" + System.getProperty("http.proxyPort"));
+            args.add("-Dhttp.proxyPort=" + proxyPort);
             args.add("-Dhttps.proxyHost=" + proxyHost);
-            args.add("-Dhttps.proxyPort=" + System.getProperty("http.proxyPort"));
-        }
+            args.add("-Dhttps.proxyPort=" + proxyPort);
+       } else {
+           log.info("Not setting proxy");
+       }
 
         // add the marker indicating the app is running in getdown
         args.add("-D" + Properties.GETDOWN + "=true");
index e89bffb..b01dfb8 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.bin;
 
 import java.lang.management.ManagementFactory;
index 5e20d76..d6d440a 100644 (file)
 package jalview.bin;
 
-import java.awt.Toolkit;
+import java.awt.HeadlessException;
 
 public class HiDPISetting
 {
-  public static final int dpi;
+  private static final int hidpiThreshold = 160;
 
-  public static final int scale;
+  private static final int hidpiMultiThreshold = 240;
 
-  private static final int hidpi = 110;
+  private static final int bigScreenThreshold = 1400;
 
-  private static final String scaleProperty = "sun.java2d.uiScale";
+  public static final String scalePropertyName = "sun.java2d.uiScale";
 
-  private static final boolean isAMac = System.getProperty("os.name")
-          .indexOf("Mac") > -1;
+  private static final boolean isLinux;
 
-  private static final boolean isLinux = System.getProperty("os.name")
-          .toLowerCase().indexOf("linux") > -1;
+  // private static final boolean isAMac;
 
-  private static final boolean isWindows = System.getProperty("os.name")
-          .toLowerCase().indexOf("windows") > -1;
+  // private static final boolean isWindows;
+
+  public static final String setHiDPIPropertyName = "setHiDPI";
+
+  public static final String setHiDPIScalePropertyName = "setHiDPIScale";
+
+  private static boolean setHiDPI = false;
+
+  private static int setHiDPIScale = 0;
+
+  public static int dpi = 0;
+
+  public static int mindimension = 0;
+
+  public static int width = 0;
+
+  public static int scale = 0;
+
+  private static boolean doneInit = false;
+
+  private static boolean allowScalePropertyArg = false;
+
+  private static ScreenInfo screenInfo = new ScreenInfo();
 
   static
   {
-    dpi = Toolkit.getDefaultToolkit().getScreenResolution();
-    scale = dpi / hidpi + 1;
-    if (scale > 1 && isLinux)
+    String system = System.getProperty("os.name") == null ? null
+            : System.getProperty("os.name").toLowerCase();
+    if (system != null)
+    {
+      isLinux = system.indexOf("linux") > -1;
+      // isAMac = system.indexOf("mac") > -1;
+      // isWindows = system.indexOf("windows") > -1;
+    }
+    else
     {
-      System.out.println("DPI detected as " + dpi
-              + ". Scaling factor set to " + scale + ".");
+      isLinux = false;
+      // isAMac = isWindows = false;
     }
   }
 
-  public static String getScalePropertyArg()
+  private static void init()
   {
-    // HiDPI setting. Just looking at Linux to start with. Test with Windows.
-    if (!isLinux)
+    if (doneInit)
     {
-      return null;
+      return;
     }
-    if (scale > 1)
+
+    // get and use command line property values first
+    String setHiDPIProperty = System.getProperty(setHiDPIPropertyName);
+    setHiDPI = setHiDPIProperty != null
+            && setHiDPIProperty.equalsIgnoreCase("true");
+
+    String setHiDPIScaleProperty = System
+            .getProperty(setHiDPIScalePropertyName);
+    if (setHiDPIScaleProperty != null)
+    {
+      try
+      {
+        setHiDPIScale = Integer.parseInt(setHiDPIScaleProperty);
+      } catch (NumberFormatException e)
+      {
+        System.err.println(setHiDPIScalePropertyName + " property give ("
+                + setHiDPIScaleProperty + ") but not parseable as integer");
+      }
+    }
+    if (setHiDPI && setHiDPIScale > 0)
+    {
+      setHiDPIScale(setHiDPIScale);
+      return;
+    }
+
+    // check to see if the scale property has already been set by something else
+    // (e.g. the OS)
+    String existingProperty = System.getProperty(scalePropertyName);
+    if (existingProperty != null)
+    {
+      try
+      {
+        int existingPropertyVal = Integer.parseInt(existingProperty);
+        System.out.println("Existing " + scalePropertyName + " is "
+                + existingPropertyVal);
+        if (existingPropertyVal > 1)
+        {
+          setHiDPIScale(existingPropertyVal);
+          return;
+        }
+      } catch (NumberFormatException e)
+      {
+        System.out.println("Could not convert property " + scalePropertyName
+                + " vale '" + existingProperty + "' to number");
+      }
+    }
+
+    // Try and auto guess a good scale based on reported DPI (not trustworthy)
+    // and screen resolution (more trustworthy)
+
+    // get screen dpi
+    screenInfo = getScreenInfo();
+    try
+    {
+      dpi = screenInfo.getScreenResolution();
+    } catch (HeadlessException e)
+    {
+      System.err.println("Cannot get screen resolution: " + e.getMessage());
+    }
+
+    // try and get screen size height and width
+    try
+    {
+      int height = screenInfo.getScreenHeight();
+      int width = screenInfo.getScreenWidth();
+      // using mindimension in case of portrait screens
+      mindimension = Math.min(height, width);
+    } catch (HeadlessException e)
+    {
+      System.err.println(
+              "Cannot get screen size height and width:" + e.getMessage());
+    }
+
+    // attempt at a formula for scaling based on screen dpi and mindimension.
+    // scale will be an integer >=1. This formula is based on some testing and
+    // guesswork!
+
+    // scale based on reported dpi. if dpi>hidpiThreshold then scale=2+multiples
+    // of hidpiMultiThreshold (else scale=1)
+    // (e.g. dpi of 110 scales 1. dpi of 120 scales 2. dpi of 360 scales 3)
+    int dpiScale = (dpi - hidpiThreshold > 0)
+            ? 2 + ((dpi - hidpiThreshold) / hidpiMultiThreshold)
+            : 1;
+
+    int dimensionScale = 1 + (mindimension / bigScreenThreshold);
+
+    // choose larger of dimensionScale or dpiScale (most likely dimensionScale
+    // as dpiScale often misreported)
+    int autoScale = Math.max(dpiScale, dimensionScale);
+
+    // only make an automatic change if scale is changed and other conditions
+    // (OS is linux) apply, or if setHiDPI has been specified
+    if ((autoScale > 1 && isLinux) || setHiDPI)
+    {
+      setHiDPIScale(autoScale);
+      return;
+    }
+
+    // looks like we're not doing any scaling
+    doneInit = true;
+  }
+
+  public static void setHiDPIScale(int s)
+  {
+    scale = s;
+    allowScalePropertyArg = true;
+    doneInit = true;
+  }
+
+  public static String getScalePropertyArg(int s)
+  {
+    return "-D" + scalePropertyName + "=" + String.valueOf(s);
+  }
+
+  public static String getScalePropertyArg()
+  {
+    init();
+    // HiDPI setting. Just looking at Linux to start with. Test with Windows.
+    return allowScalePropertyArg ? getScalePropertyArg(scale) : null;
+  }
+
+  public static void clear()
+  {
+    setHiDPI = false;
+    setHiDPIScale = 0;
+    dpi = 0;
+    mindimension = 0;
+    width = 0;
+    scale = 0;
+    doneInit = false;
+    allowScalePropertyArg = false;
+  }
+
+  public static void setScreenInfo(ScreenInfo si)
+  {
+    screenInfo = si;
+  }
+
+  public static ScreenInfo getScreenInfo()
+  {
+    if (screenInfo == null)
     {
-      return "-D" + scaleProperty + "=" + scale;
+      screenInfo = new ScreenInfo();
     }
-    return null;
+    return screenInfo;
   }
 }
index 7849ba2..5d7f14c 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.bin;
 
 /**
@@ -21,9 +41,9 @@ public class MemorySetting
 
   private static final long GIGABYTE = 1073741824; // 1GB
 
-  public static final long LEAVE_FREE_MIN_MEMORY = GIGABYTE/2;
+  public static final long LEAVE_FREE_MIN_MEMORY = GIGABYTE / 2;
 
-  public static final long APPLICATION_MIN_MEMORY = GIGABYTE/2;
+  public static final long APPLICATION_MIN_MEMORY = GIGABYTE / 2;
 
   private static final long MAX_HEAPSIZE_GB_DEFAULT = 32;
 
@@ -41,28 +61,27 @@ public class MemorySetting
    * values: jvmmempc - the maximum percentage of total physical memory to
    * allocate, and jvmmemmax - the maximum absolute amount of physical memory to
    * allocate. These can be provided as arguments. If not provided as arguments
-   * (or set as null) system properties will be used instead (if set). The memory
-   * setting returned will be the lower of the two values. If either of the values
-   * are not provided then defaults will be used (jvmmempc=90, jvmmemmax=32GB). If
-   * total physical memory can't be ascertained when jvmmempc was set or neither
-   * jvmmempc nor jvmmemmax were set, then jvmmemmax defaults to a much safer 8GB.
-   * In this case explicitly setting jvmmemmax and not setting jvmmempc can set a
-   * higher memory for Jalview. The calculation also tries to ensure 0.5GB memory
-   * for the OS, but also tries to ensure at least 0.5GB memory for Jalview (which
-   * takes priority over the OS) If there is less then 0.5GB of physical memory
-   * then the total physical memory is used for Jalview.
+   * (or set as null) system properties will be used instead (if set). The
+   * memory setting returned will be the lower of the two values. If either of
+   * the values are not provided then defaults will be used (jvmmempc=90,
+   * jvmmemmax=32GB). If total physical memory can't be ascertained when
+   * jvmmempc was set or neither jvmmempc nor jvmmemmax were set, then jvmmemmax
+   * defaults to a much safer 8GB. In this case explicitly setting jvmmemmax and
+   * not setting jvmmempc can set a higher memory for Jalview. The calculation
+   * also tries to ensure 0.5GB memory for the OS, but also tries to ensure at
+   * least 0.5GB memory for Jalview (which takes priority over the OS) If there
+   * is less then 0.5GB of physical memory then the total physical memory is
+   * used for Jalview.
    * 
    * @param jvmmemmaxarg
-   *                       Maximum value of memory to set. This can be a numeric
-   *                       string optionally followed by "b", "k", "m", "g", "t"
-   *                       (case insensitive) to indicate bytes, kilobytes,
-   *                       megabytes, gigabytes, terabytes respectively. If null a
-   *                       default value of 32G will be used. If null and either
-   *                       physical memory can't be determined then the default is
-   *                       8GB.
+   *          Maximum value of memory to set. This can be a numeric string
+   *          optionally followed by "b", "k", "m", "g", "t" (case insensitive)
+   *          to indicate bytes, kilobytes, megabytes, gigabytes, terabytes
+   *          respectively. If null a default value of 32G will be used. If null
+   *          and either physical memory can't be determined then the default is
+   *          8GB.
    * @param jvmmempcarg
-   *                       Max percentage of physical memory to use. Defaults to
-   *                       "90".
+   *          Max percentage of physical memory to use. Defaults to "90".
    * 
    * @return The amount of memory (in bytes) to allocate to Jalview
    */
@@ -116,9 +135,8 @@ public class MemorySetting
       {
         memmax = MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE;
         System.out.println("MemorySetting Property '"
-                + MAX_HEAPSIZE_PROPERTY_NAME
-                + "' ("
-                + jvmmemmaxarg + "') badly formatted, using default ("
+                + MAX_HEAPSIZE_PROPERTY_NAME + "' (" + jvmmemmaxarg
+                + "') badly formatted, using default ("
                 + MAX_HEAPSIZE_GB_DEFAULT + "g).");
       }
 
@@ -126,11 +144,10 @@ public class MemorySetting
       if (Long.MAX_VALUE / memmax < multiplier)
       {
         memmax = MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE;
-        System.out.println(
-                "MemorySetting Property '" + MAX_HEAPSIZE_PROPERTY_NAME + "' ("
-                        + jvmmemmaxarg
-                        + ") too big, using default ("
-                        + MAX_HEAPSIZE_GB_DEFAULT + "g).");
+        System.out.println("MemorySetting Property '"
+                + MAX_HEAPSIZE_PROPERTY_NAME + "' (" + jvmmemmaxarg
+                + ") too big, using default (" + MAX_HEAPSIZE_GB_DEFAULT
+                + "g).");
       }
       else
       {
@@ -141,11 +158,10 @@ public class MemorySetting
       if (memmax < APPLICATION_MIN_MEMORY)
       {
         memmax = APPLICATION_MIN_MEMORY;
-        System.out.println(
-                "MemorySetting Property '" + MAX_HEAPSIZE_PROPERTY_NAME + "' ("
-                        + jvmmemmaxarg
-                        + ") too small, using minimum ("
-                        + APPLICATION_MIN_MEMORY + ").");
+        System.out.println("MemorySetting Property '"
+                + MAX_HEAPSIZE_PROPERTY_NAME + "' (" + jvmmemmaxarg
+                + ") too small, using minimum (" + APPLICATION_MIN_MEMORY
+                + ").");
       }
 
     }
@@ -176,18 +192,17 @@ public class MemorySetting
         }
         else
         {
-          System.out.println(
-                  "MemorySetting Property '"
-                          + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME
-                          + "' should be in range 1..100. Using default "
-                          + percent + "%");
+          System.out.println("MemorySetting Property '"
+                  + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME
+                  + "' should be in range 1..100. Using default " + percent
+                  + "%");
         }
       }
     } catch (NumberFormatException e)
     {
-      System.out.println(
-              "MemorySetting property '" + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME
-                      + "' (" + jvmmempcarg + ") badly formatted");
+      System.out.println("MemorySetting property '"
+              + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' (" + jvmmempcarg
+              + ") badly formatted");
     }
 
     // catch everything in case of no com.sun.management.OperatingSystemMXBean
@@ -226,9 +241,9 @@ public class MemorySetting
           else
           {
             System.out.println("MemorySetting Property '"
-                    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' (" + jvmmempcarg
-                    + ") too small. Using minimum (" + APPLICATION_MIN_MEMORY
-                    + ").");
+                    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' ("
+                    + jvmmempcarg + ") too small. Using minimum ("
+                    + APPLICATION_MIN_MEMORY + ").");
           }
           mempc = APPLICATION_MIN_MEMORY;
         }
@@ -257,7 +272,12 @@ public class MemorySetting
     // jvmmempc was set OR neither jvmmempc nor jvmmemmax were set), let's cap
     // maxMemLong to 8GB
     if (memoryPercentError && mempc == -1
-            && !(jvmmempcarg == null && jvmmemmaxarg != null) // the same as (jvmmempcarg != null || (jvmmempcarg == null && jvmmemmaxarg
+            && !(jvmmempcarg == null && jvmmemmaxarg != null) // the same as
+                                                              // (jvmmempcarg !=
+                                                              // null ||
+                                                              // (jvmmempcarg ==
+                                                              // null &&
+                                                              // jvmmemmaxarg
                                                               // == null))
             && memmax > NOMEM_MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE)
     {
diff --git a/getdown/src/getdown/core/src/main/java/jalview/bin/ScreenInfo.java b/getdown/src/getdown/core/src/main/java/jalview/bin/ScreenInfo.java
new file mode 100644 (file)
index 0000000..899bf94
--- /dev/null
@@ -0,0 +1,21 @@
+package jalview.bin;
+
+import java.awt.Toolkit;
+
+public class ScreenInfo
+{
+  public int getScreenResolution()
+  {
+    return Toolkit.getDefaultToolkit().getScreenResolution();
+  }
+
+  public int getScreenHeight()
+  {
+    return (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
+  }
+
+  public int getScreenWidth()
+  {
+    return (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+  }
+}
index 6af81a9..b5e68f2 100644 (file)
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.threerings.getdown</groupId>
     <artifactId>getdown</artifactId>
-    <version>1.8.3-1.2.8_FJVL</version>
+    <version>1.8.3-1.2.10_FJVL</version>
   </parent>
 
   <artifactId>getdown-launcher</artifactId>
index 6558cb9..65e5fb9 100755 (executable)
@@ -3,7 +3,7 @@
 if [ x$JVLVERSION != x ]; then
   export VERSION=$JVLVERSION
 else
-  export VERSION=1.8.3-1.2.8_JVL
+  export VERSION=1.8.3-1.2.10_JVL
 fi
 
 if [ x${VERSION%_JVL} = x$VERSION ]; then
index 4585d06..7a0fd27 100644 (file)
@@ -10,7 +10,7 @@
   <groupId>com.threerings.getdown</groupId>
   <artifactId>getdown</artifactId>
   <packaging>pom</packaging>
-  <version>1.8.3-1.2.8_FJVL</version>
+  <version>1.8.3-1.2.10_FJVL</version>
 
   <name>getdown</name>
   <description>An application installer and updater.</description>
index 5fda2df..58b06db 100755 (executable)
     browser application.
   </p>
   <p>
-    <em>Proxy Server</em><br> If you normally use a proxy server
-    for using the internet, you must tick the box &quot;Use a Proxy
-    Server&quot; and enter the address and port details as necessary.
+    <em>Proxy Server</em><br>
+    There are three settings to choose from:<br>
+    <ul>
+           <li><em>No proxy servers</em> will configure Jalview to use a
+                   direct internet connection.</li>
+           <li><em>System proxy servers</em> will configure Jalview to use
+                   the proxy server passed to it by your system at startup.</li>
+           <li><em>Use these proxy servers:</em> allows you to set a custom
+                   proxy server.</li>
+    </ul>
+    If you normally use a proxy server for using the internet, you must
+    choose one of <em>System proxy servers</em>, or if these have not been
+    passed correctly you should set your own proxy servers to use by selecting
+    <em>Use these proxy servers</em>.
+    You will then need to enter the host and port details as necessary.
     Web Services will not work if you are using a proxy server and do
-    not enter the settings here.
+    not choose the system proxy or enter your own settings here.<br>
+    There are separate host and port settings for HTTP and HTTPS proxies.
+    Often these are the same but you should enter the host and port into both
+    rows.<br>
+    You can also check the <em>Authentication required</em> box if your proxy
+    requires username and password authentication.  You can enter both the
+    <em>Username</em> and <em>Password</em> but only the <em>Username</em> will
+    be stored in Jalview's preferences file, the password will only be stored
+    until the end of the current Jalview session.<br>
+    This means that if the proxy settings are still valid, Jalview will ask for
+    the password when it starts the next session.
   </p>
   <p>
     <em>Usage statistics, Questionnaire and Version checks</em><br>
index f800c5c..dadce6e 100644 (file)
Binary files a/j11lib/getdown-core.jar and b/j11lib/getdown-core.jar differ
index f800c5c..dadce6e 100644 (file)
Binary files a/j8lib/getdown-core.jar and b/j8lib/getdown-core.jar differ
index dbf54bf..d8217d2 100644 (file)
@@ -372,7 +372,7 @@ label.example = Example
 label.example_param = Example: {0}
 label.select_file_format_before_saving = You must select a file format before saving!
 label.file_format_not_specified = File format not specified
-label.couldnt_save_file = Couldn't save file: {0}
+label.couldnt_save_file = Couldn''t save file: {0}
 label.error_saving_file = Error Saving File
 label.remove_from_default_list = Remove from default list?
 label.remove_user_defined_colour = Remove user defined colour
@@ -577,13 +577,21 @@ label.gap_symbol = Gap Symbol
 label.prot_alignment_colour = Protein Alignment Colour
 label.nuc_alignment_colour = Nucleotide Alignment Colour
 label.address = Address
+label.host = Host
 label.port = Port
 label.default_browser_unix = Default Browser (Unix)
 label.send_usage_statistics = Send usage statistics
 label.check_for_questionnaires = Check for questionnaires
 label.check_for_latest_version = Check for latest version
 label.url_linkfrom_sequence_id = URL link from Sequence ID
-label.use_proxy_server = Use a proxy server
+label.no_proxy = No proxy servers
+label.system_proxy = System proxy servers (http={0}; https={1})
+label.use_proxy_server = Use these proxy servers
+label.auth_required = Authentication required
+label.username = Username
+label.password = Password
+label.proxy_password_required = Proxy password required
+label.not_stored = not stored in Preferences file
 label.rendering_style = {0} rendering style
 label.append_start_end = Append /start-end (/15-380)
 label.full_sequence_id = Full Sequence Id
@@ -620,7 +628,7 @@ label.delete_service_url = Delete Service URL
 label.details = Details
 label.options = Options
 label.parameters = Parameters
-label.proxy_server = Proxy Server
+label.proxy_servers = Proxy Servers
 label.file_output = File Output
 label.select_input_type = Select input type
 label.set_options_for_type = Set options for type
@@ -1312,6 +1320,7 @@ label.backupfiles_confirm_save_file = Confirm save file
 label.backupfiles_confirm_save_file_backupfiles_roll_wrong = Something possibly went wrong with the backups of this file.
 label.backupfiles_confirm_save_new_saved_file_ok = The new saved file seems okay.
 label.backupfiles_confirm_save_new_saved_file_not_ok = The new saved file might not be okay.
+label.continue_operation = Continue operation?
 label.backups = Backups
 label.backup = Backup
 label.backup_files = Backup Files
@@ -1377,3 +1386,7 @@ label.features_not_shown = {0} feature(s) not shown
 label.no_features_to_sort_by = No features to sort by
 label.ignore_hidden = Ignore hidden columns
 label.ignore_hidden_tooltip = Ignore any characters in hidden columns when matching
+label.log_level = Log level
+label.log_level_tooltip = Temporarily set the log level for this console. The log level will revert to {0} when this Java console is closed.
+label.copy_to_clipboard = Copy to clipboard
+label.copy_to_clipboard_tooltip = Copy all of the log text in this console to the system clipboard
index 7383a0e..fdf4201 100644 (file)
@@ -531,13 +531,19 @@ label.database_references = Referencias a base de datos
 #label.scroll_highlighted_regions = Desplazarse hasta las regiones resaltadas
 label.gap_symbol = Símbolo del hueco
 label.address = Dirección
+label.host = Host
 label.port = Puerto
 label.default_browser_unix = Navegador por defecto (Unix)
 label.send_usage_statistics = Enviar estadísticas de uso
 label.check_for_questionnaires = Comprobar los cuestionarios
 label.check_for_latest_version = Comprobar la Ãºltima versión
 label.url_linkfrom_sequence_id = URL del enlace del ID de la secuencia
-label.use_proxy_server = Utilizar un servidor proxy
+label.no_proxy = Sin servidores proxy
+label.system_proxy = Servidores proxy del sistema (http={0}; https={1})
+label.use_proxy_server = Utilizar estos servidores proxy
+label.auth_required = Autenticacion requerida
+label.username = Usario
+label.password = Contraseña
 label.rendering_style = Estilo de visualización {0}
 label.append_start_end = Añadir /inicio-fin (/15-380)
 label.full_sequence_id = ID de la secuencia completo
@@ -572,7 +578,7 @@ label.delete_service_url = Borrar la URL del servicio
 label.details = Detalles
 label.options = Opciones
 label.parameters = Paramétros
-label.proxy_server = Servidor proxy
+label.proxy_servers = Servidores proxy
 label.file_output = Fichero de salida
 label.select_input_type = Seleccionar el tipo de entrada
 label.set_options_for_type = Establecer opciones para el tipo
@@ -1313,6 +1319,7 @@ label.backupfiles_confirm_save_file = Confirmar guardar archivo
 label.backupfiles_confirm_save_file_backupfiles_roll_wrong = Posiblemente algo está mal con los archivos de respaldos.
 label.backupfiles_confirm_save_new_saved_file_ok = El nuevo archivo guardado parece estar bien.
 label.backupfiles_confirm_save_new_saved_file_not_ok = El nuevo archivo guardado podría no estar bien.
+label.continue_operation = Â¿Continuar operación?
 label.backups = Respaldos
 label.backup = Respaldo
 label.backup_files = Archivos de respaldos
@@ -1378,3 +1385,7 @@ label.features_not_shown = {0} caracter
 label.no_features_to_sort_by = No hay características para ordenar
 label.ignore_hidden = Ignorar columnas ocultas
 label.ignore_hidden_tooltip = Ignorar caracteres en columnas ocultas
+label.log_level = Nivel del registro
+label.log_level_tooltip = Establezca temporalmente el nivel de registro para esta consola. El nivel de registro volverá a {0} cuando se cierre esta consola de Java.
+label.copy_to_clipboard = Copiar en el portapapeles
+label.copy_to_clipboard_tooltip = Copie todo el texto de registro en esta consola al portapapeles del sistema
index ff475b6..dc71f91 100755 (executable)
  */
 package jalview.bin;
 
-import jalview.datamodel.PDBEntry;
-import jalview.gui.UserDefinedColours;
-import jalview.schemes.ColourSchemeLoader;
-import jalview.schemes.ColourSchemes;
-import jalview.schemes.UserColourScheme;
-import jalview.structure.StructureImportSettings;
-import jalview.urls.IdOrgSettings;
-import jalview.util.ColorUtils;
-import jalview.util.Platform;
-import jalview.ws.sifts.SiftsSettings;
-
 import java.awt.Color;
 import java.io.BufferedReader;
 import java.io.File;
@@ -38,9 +27,14 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
 import java.net.URL;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
@@ -58,6 +52,19 @@ import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import org.apache.log4j.SimpleLayout;
 
+import jalview.datamodel.PDBEntry;
+import jalview.gui.Preferences;
+import jalview.gui.UserDefinedColours;
+import jalview.schemes.ColourSchemeLoader;
+import jalview.schemes.ColourSchemes;
+import jalview.schemes.UserColourScheme;
+import jalview.structure.StructureImportSettings;
+import jalview.urls.IdOrgSettings;
+import jalview.util.ColorUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.sifts.SiftsSettings;
+
 /**
  * Stores and retrieves Jalview Application Properties Lists and fields within
  * list entries are separated by '|' symbols unless otherwise stated (|) clauses
@@ -269,6 +276,28 @@ public class Cache
    */
   public static Logger log;
 
+  // save the proxy properties set at startup
+  public final static String[] startupProxyProperties = {
+      System.getProperty("http.proxyHost"),
+      System.getProperty("http.proxyPort"),
+      System.getProperty("https.proxyHost"),
+      System.getProperty("https.proxyPort"),
+      System.getProperty("http.proxyUser"),
+      System.getProperty("http.proxyPassword"),
+      System.getProperty("https.proxyUser"),
+      System.getProperty("https.proxyPassword"),
+      System.getProperty("http.nonProxyHosts") };
+
+  public final static String PROXYTYPE_NONE = "none";
+
+  // "false" and "true" for backward compatibility
+  public final static String PROXYTYPE_SYSTEM = "false";
+
+  public final static String PROXYTYPE_CUSTOM = "true";
+
+  // in-memory only storage of proxy password, safer to use char array
+  public static char[] proxyAuthPassword = null;
+
   /** Jalview Properties */
   public static Properties applicationProperties = new Properties()
   {
@@ -318,7 +347,8 @@ public class Cache
       // lcastor.setLevel(Level.toLevel(Cache.getDefault("logs.Castor.Level",
       // Level.INFO.toString())));
       // we shouldn't need to do this
-      org.apache.log4j.Logger.getRootLogger().setLevel(org.apache.log4j.Level.INFO); 
+      org.apache.log4j.Logger.getRootLogger()
+              .setLevel(org.apache.log4j.Level.INFO);
 
       jalview.bin.Cache.log.setLevel(Level.toLevel(Cache
               .getDefault("logs.Jalview.level", Level.INFO.toString())));
@@ -364,7 +394,7 @@ public class Cache
         InputStream fis;
         try
         {
-          fis = new java.net.URL(propertiesFile).openStream();
+          fis = new URL(propertiesFile).openStream();
           System.out.println(
                   "Loading jalview properties from : " + propertiesFile);
           System.out.println(
@@ -391,16 +421,43 @@ public class Cache
         System.out.println("Error reading properties file: " + ex);
       }
     }
+
+    /* TO BE REPLACED WITH PROXY_TYPE SETTINGS 
     if (getDefault("USE_PROXY", false))
     {
       String proxyServer = getDefault("PROXY_SERVER", ""),
               proxyPort = getDefault("PROXY_PORT", "8080");
+    }
+    */
 
-      System.out.println("Using proxyServer: " + proxyServer
-              + " proxyPort: " + proxyPort);
-
-      System.setProperty("http.proxyHost", proxyServer);
-      System.setProperty("http.proxyPort", proxyPort);
+    // PROXY TYPE settings (now three options "none", "false", "true", but using
+    // backward compatible strings)
+    String proxyType = getDefault("USE_PROXY", PROXYTYPE_SYSTEM);
+    // default to upgrading old settings
+    switch (proxyType)
+    {
+    case PROXYTYPE_NONE:
+      clearProxyProperties();
+      break;
+    case PROXYTYPE_SYSTEM: // use system settings
+      resetProxyProperties();
+      break;
+    case PROXYTYPE_CUSTOM: // use specified proxy settings
+      String httpHost = getDefault("PROXY_SERVER", "");
+      String httpPort = getDefault("PROXY_PORT", "8080");
+      String httpsHost = getDefault("PROXY_SERVER_HTTPS", httpHost);
+      String httpsPort = getDefault("PROXY_PORT_HTTPS", httpPort);
+      String httpUser = getDefault("PROXY_AUTH_USER", null);
+      // https.proxyUser and https.proxyPassword are not able to be
+      // independently set in Preferences yet (or http.nonProxyHosts)
+      String httpsUser = getDefault("PROXY_AUTH_USER_HTTPS", httpUser);
+      setProxyProperties(httpHost, httpPort, httpsHost, httpsPort, httpUser,
+              proxyAuthPassword, httpsUser, proxyAuthPassword, "localhost");
+      break;
+    default:
+      String message = "Incorrect PROXY_TYPE - should be 'none' (clear proxy properties), 'false' (system settings), 'true' (custom settings): "
+              + proxyType;
+      Cache.warn(message);
     }
 
     // LOAD THE AUTHORS FROM THE authors.props file
@@ -422,9 +479,9 @@ public class Cache
     }
     if (authorDetails == null)
     {
-        applicationProperties.remove("AUTHORS");
-        applicationProperties.remove("AUTHORFNAMES");
-        applicationProperties.remove("YEAR");
+      applicationProperties.remove("AUTHORS");
+      applicationProperties.remove("AUTHORFNAMES");
+      applicationProperties.remove("YEAR");
     }
 
     loadBuildProperties(false);
@@ -457,15 +514,16 @@ public class Cache
 
     String jnlpVersion = System.getProperty("jalview.version");
 
-    // jnlpVersion will be null if a latest version check for the channel needs to
-    // be done
+    // jnlpVersion will be null if a latest version check for the channel needs
+    // to be done
     // Dont do this check if running in headless mode
 
     if (jnlpVersion == null && getDefault("VERSION_CHECK", true)
             && (System.getProperty("java.awt.headless") == null || System
                     .getProperty("java.awt.headless").equals("false")))
     {
-      new Thread()
+
+      class VersionChecker extends Thread
       {
         @Override
         public void run()
@@ -513,7 +571,10 @@ public class Cache
 
           setProperty("LATEST_VERSION", remoteVersion);
         }
-      }.start();
+      }
+
+      VersionChecker vc = new VersionChecker();
+      vc.start();
     }
     else
     {
@@ -550,8 +611,8 @@ public class Cache
         url = Cache.class.getResource(resourcePath).toString();
       } catch (Exception ex)
       {
-        System.err.println("Failed to resolve resource " + resourcePath + ": "
-                + ex.getMessage());
+        System.err.println("Failed to resolve resource " + resourcePath
+                + ": " + ex.getMessage());
       }
     }
     else
@@ -600,8 +661,8 @@ public class Cache
     new BuildDetails(codeVersion, null, codeInstallation);
     if (printVersion && reportVersion)
     {
-      System.out
-            .println("Jalview Version: " + codeVersion + codeInstallation);
+      System.out.println(
+              "Jalview Version: " + codeVersion + codeInstallation);
     }
   }
 
@@ -1145,16 +1206,23 @@ public class Cache
   public static String getVersionDetailsForConsole()
   {
     StringBuilder sb = new StringBuilder();
-    sb.append("Jalview Version: " + jalview.bin.Cache.getDefault("VERSION", "TEST"));
+    sb.append("Jalview Version: ");
+    sb.append(jalview.bin.Cache.getDefault("VERSION", "TEST"));
     sb.append("\n");
-    sb.append("Jalview Installation: "
-            + jalview.bin.Cache.getDefault("INSTALLATION", "unknown"));
+    sb.append("Jalview Installation: ");
+    sb.append(jalview.bin.Cache.getDefault("INSTALLATION", "unknown"));
     sb.append("\n");
-    sb.append("Build Date: " + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
+    sb.append("Build Date: ");
+    sb.append(jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
     sb.append("\n");
-    sb.append("Java version: " + System.getProperty("java.version"));
+    sb.append("Java version: ");
+    sb.append(System.getProperty("java.version"));
     sb.append("\n");
-    sb.append(System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version"));
+    sb.append(System.getProperty("os.arch"));
+    sb.append(" ");
+    sb.append(System.getProperty("os.name"));
+    sb.append(" ");
+    sb.append(System.getProperty("os.version"));
     sb.append("\n");
     appendIfNotNull(sb, "Install4j version: ",
             System.getProperty("sys.install4jVersion"), "\n", null);
@@ -1163,11 +1231,18 @@ public class Cache
     appendIfNotNull(sb, "Launcher version: ",
             System.getProperty("launcher_version"), "\n", null);
     LookAndFeel laf = UIManager.getLookAndFeel();
-    String lafName = laf == null?"Not obtained":laf.getName();
-    String lafClass = laf == null?"unknown":laf.getClass().getName();
-    appendIfNotNull(sb, "LookAndFeel: ", lafName+" ("+lafClass+")", "\n", null);
-    // Not displayed in release version ( determined by possible version number regex 9[9.]*9[.-_a9]* )
-    if (Pattern.matches("^\\d[\\d\\.]*\\d[\\.\\-\\w]*$", jalview.bin.Cache.getDefault("VERSION", "TEST"))) {
+    String lafName = laf == null ? "Not obtained" : laf.getName();
+    String lafClass = laf == null ? "unknown" : laf.getClass().getName();
+    sb.append("LookAndFeel: ");
+    sb.append(lafName);
+    sb.append(" (");
+    sb.append(lafClass);
+    sb.append(")\n");
+    // Not displayed in release version ( determined by possible version number
+    // regex 9[9.]*9[.-_a9]* )
+    if (Pattern.matches("^\\d[\\d\\.]*\\d[\\.\\-\\w]*$",
+            jalview.bin.Cache.getDefault("VERSION", "TEST")))
+    {
       appendIfNotNull(sb, "Getdown appdir: ",
               System.getProperty("getdownappdir"), "\n", null);
       appendIfNotNull(sb, "Java home: ", System.getProperty("java.home"),
@@ -1186,4 +1261,340 @@ public class Cache
     // eg 'built from Source' or update channel
     return jalview.bin.Cache.getDefault("INSTALLATION", "unknown");
   }
-}
+
+  public static String getStackTraceString(Throwable t)
+  {
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    t.printStackTrace(pw);
+    return sw.toString();
+  }
+
+  // proxy properties methods
+  public static void clearProxyProperties()
+  {
+    setProxyProperties(null, null, null, null, null, null, null, null,
+            null);
+  }
+
+  public static void resetProxyProperties()
+  {
+    setProxyProperties(startupProxyProperties[0], startupProxyProperties[1],
+            startupProxyProperties[2], startupProxyProperties[3],
+            startupProxyProperties[4],
+            startupProxyProperties[5] == null ? null
+                    : startupProxyProperties[5].toCharArray(),
+            startupProxyProperties[6],
+            startupProxyProperties[7] == null ? null
+                    : startupProxyProperties[7].toCharArray(),
+            startupProxyProperties[8]);
+    StringBuilder sb = new StringBuilder();
+    sb.append("Setting proxy properties to: http.proxyHost=")
+            .append(startupProxyProperties[0]).append(", http.proxyPort=")
+            .append(startupProxyProperties[1])
+            .append(startupProxyProperties[4] != null
+                    && !startupProxyProperties[4].isEmpty()
+                            ? " [" + startupProxyProperties[4] + "]"
+                            : "")
+            .append(", https.proxyHost=").append(startupProxyProperties[2])
+            .append(", https.proxyPort=").append(startupProxyProperties[3])
+            .append(startupProxyProperties[6] != null
+                    && !startupProxyProperties[6].isEmpty()
+                            ? " [" + startupProxyProperties[6] + "]"
+                            : "");
+
+    Cache.debug(sb.toString());
+  }
+
+  public static void setProxyPropertiesFromPreferences()
+  {
+    setProxyPropertiesFromPreferences(Cache.PROXYTYPE_SYSTEM);
+  }
+
+  public static void setProxyPropertiesFromPreferences(
+          String previousProxyType)
+  {
+    String proxyType = Cache.getDefault("USE_PROXY",
+            Cache.PROXYTYPE_SYSTEM);
+    if (previousProxyType != null
+            && !proxyType.equals(Cache.PROXYTYPE_CUSTOM) // always apply
+                                                         // customProxy
+            && proxyType.equals(previousProxyType))
+    {
+      // no change
+      return;
+    }
+    switch (proxyType)
+    {
+    case Cache.PROXYTYPE_NONE:
+      if (!previousProxyType.equals(proxyType))
+      {
+        Cache.log.info("Setting no proxy settings");
+        Cache.setProxyProperties(null, null, null, null, null, null, null,
+                null, null);
+      }
+      break;
+    case Cache.PROXYTYPE_CUSTOM:
+      // always re-set a custom proxy -- it might have changed, particularly
+      // password
+      Cache.log.info("Setting custom proxy settings");
+      boolean proxyAuthSet = Cache.getDefault("PROXY_AUTH", false);
+      Cache.setProxyProperties(Cache.getDefault("PROXY_SERVER", null),
+              Cache.getDefault("PROXY_PORT", null),
+              Cache.getDefault("PROXY_SERVER_HTTPS", null),
+              Cache.getDefault("PROXY_PORT_HTTPS", null),
+              proxyAuthSet ? Cache.getDefault("PROXY_AUTH_USERNAME", "")
+                      : null,
+              proxyAuthSet ? Cache.proxyAuthPassword : null,
+              proxyAuthSet ? Cache.getDefault("PROXY_AUTH_USERNAME", "")
+                      : null,
+              proxyAuthSet ? Cache.proxyAuthPassword : null, "localhost");
+      break;
+    default: // system proxy settings by default
+      Cache.log.info("Setting system proxy settings");
+      Cache.resetProxyProperties();
+    }
+  }
+
+  public static void setProxyProperties(String httpHost, String httpPort,
+          String httpsHost, String httpsPort, String httpUser,
+          char[] httpPassword, String httpsUser, char[] httpsPassword,
+          String nonProxyHosts)
+  {
+    setOrClearSystemProperty("http.proxyHost", httpHost);
+    setOrClearSystemProperty("http.proxyPort", httpPort);
+    setOrClearSystemProperty("https.proxyHost", httpsHost);
+    setOrClearSystemProperty("https.proxyPort", httpsPort);
+    setOrClearSystemProperty("http.proxyUser", httpUser);
+    setOrClearSystemProperty("https.proxyUser", httpsUser);
+    // note: passwords for http.proxyPassword and https.proxyPassword are sent
+    // via the Authenticator, properties do not need to be set
+
+    // are we using a custom proxy (password prompt might be required)?
+    boolean customProxySet = getDefault("USE_PROXY", PROXYTYPE_SYSTEM)
+            .equals(PROXYTYPE_CUSTOM);
+
+    /*
+     * A bug in Java means the AuthCache does not get reset, so once it has working credentials,
+     * it never asks for more, so changing the Authenticator has no effect (as getPasswordAuthentication()
+     * is not re-called).
+     * This could lead to password leak to a hostile proxy server, so I'm putting in a hack to clear
+     * the AuthCache.
+     * see https://www.generacodice.com/en/articolo/154918/Reset-the-Authenticator-credentials
+     * ...
+     * Turns out this is only accessible in Java 8, and not in Java 9 onwards, so commenting out
+     */
+    /*
+    try
+    {
+      sun.net.www.protocol.http.AuthCacheValue
+              .setAuthCache(new sun.net.www.protocol.http.AuthCacheImpl());
+    } catch (Throwable t)
+    {
+      Cache.error(t.getMessage());
+      Cache.debug(getStackTraceString(t));
+    }
+    */
+
+    if (httpUser != null || httpsUser != null)
+    {
+      try
+      {
+        char[] displayHttpPw = new char[httpPassword == null ? 0
+                : httpPassword.length];
+        Arrays.fill(displayHttpPw, '*');
+        Cache.debug("CACHE Proxy: setting new Authenticator with httpUser='"
+                + httpUser + "' httpPassword='" + displayHttpPw + "'");
+        Authenticator.setDefault(new Authenticator()
+        {
+          @Override
+          protected PasswordAuthentication getPasswordAuthentication()
+          {
+            if (getRequestorType() == RequestorType.PROXY)
+            {
+              String protocol = getRequestingProtocol();
+              boolean needProxyPasswordSet = false;
+              if (customProxySet &&
+              // we have a username but no password for the scheme being
+              // requested
+              (protocol.equalsIgnoreCase("http")
+                      && (httpUser != null && httpUser.length() > 0
+                              && (httpPassword == null
+                                      || httpPassword.length == 0)))
+                      || (protocol.equalsIgnoreCase("https")
+                              && (httpsUser != null
+                                      && httpsUser.length() > 0
+                                      && (httpsPassword == null
+                                              || httpsPassword.length == 0))))
+              {
+                // open Preferences -> Connections
+                String message = MessageManager
+                        .getString("label.proxy_password_required");
+                Preferences.openPreferences(Preferences.CONNECTIONS_TAB,
+                        message);
+                Preferences.getInstance()
+                        .proxyAuthPasswordCheckHighlight(true, true);
+              }
+              else
+              {
+                try
+                {
+                  if (protocol.equalsIgnoreCase("http")
+                          && getRequestingHost().equalsIgnoreCase(httpHost)
+                          && getRequestingPort() == Integer
+                                  .valueOf(httpPort))
+                  {
+                    Cache.debug(
+                            "AUTHENTICATOR returning PasswordAuthentication(\""
+                                    + httpUser + "\", '"
+                                    + new String(displayHttpPw) + "')");
+                    return new PasswordAuthentication(httpUser,
+                            httpPassword);
+                  }
+                  if (protocol.equalsIgnoreCase("https")
+                          && getRequestingHost().equalsIgnoreCase(httpsHost)
+                          && getRequestingPort() == Integer
+                                  .valueOf(httpsPort))
+                  {
+                    char[] displayHttpsPw = new char[httpPassword.length];
+                    Arrays.fill(displayHttpsPw, '*');
+                    Cache.debug(
+                            "AUTHENTICATOR returning PasswordAuthentication(\""
+                                    + httpsUser + "\", '" + displayHttpsPw
+                                    + "'");
+                    return new PasswordAuthentication(httpsUser,
+                            httpsPassword);
+                  }
+                } catch (NumberFormatException e)
+                {
+                  Cache.error("Problem with proxy port values [http:"
+                          + httpPort + ", https:" + httpsPort + "]");
+                }
+                Cache.debug(
+                        "AUTHENTICATOR after trying to get PasswordAuthentication");
+              }
+            }
+            // non proxy request
+            Cache.debug("AUTHENTICATOR returning null");
+            return null;
+          }
+        });
+        // required to re-enable basic authentication (should be okay for a
+        // local proxy)
+        Cache.debug(
+                "AUTHENTICATOR setting property 'jdk.http.auth.tunneling.disabledSchemes' to \"\"");
+        System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
+      } catch (SecurityException e)
+      {
+        Cache.error("Could not set default Authenticator");
+        Cache.debug(getStackTraceString(e));
+      }
+    }
+    else
+    {
+      // reset the Authenticator to protect http.proxyUser and
+      // http.proxyPassword Just In Case
+      /* as noted above, due to bug in java this doesn't work if the sun.net.www.protocol.http.AuthCache
+       * has working credentials. No workaround for Java 11.
+       */
+      Cache.debug("AUTHENTICATOR setting default Authenticator to null");
+      Authenticator.setDefault(null);
+    }
+
+    // nonProxyHosts not currently configurable in Preferences
+    Cache.debug("AUTHENTICATOR setting property 'http.nonProxyHosts' to \""
+            + nonProxyHosts + "\"");
+    setOrClearSystemProperty("http.nonProxyHosts", nonProxyHosts);
+  }
+
+  public static void setOrClearSystemProperty(String key, char[] value)
+  {
+    setOrClearSystemProperty(key,
+            (value == null) ? null : new String(value));
+  }
+
+  public static void setOrClearSystemProperty(String key, String value)
+  {
+    if (key == null)
+    {
+      return;
+    }
+    if (value == null)
+    {
+      System.clearProperty(key);
+    }
+    else
+    {
+      System.setProperty(key, value);
+    }
+  }
+
+  public final static int TRACE = 10;
+
+  public final static int DEBUG = 20;
+
+  public final static int INFO = 30;
+
+  public final static int WARN = 40;
+
+  public final static int ERROR = 50;
+
+  public static boolean println(int level, String message)
+  {
+    if (Cache.log == null)
+    {
+      if (level >= WARN)
+        System.err.println(message);
+      else if (level >= INFO)
+        System.out.println(message);
+      // not printing debug or trace messages
+      return false;
+    }
+    if (level >= ERROR)
+    {
+      Cache.log.error(message);
+    }
+    else if (level >= WARN)
+    {
+      Cache.log.warn(message);
+    }
+    else if (level >= INFO)
+    {
+      Cache.log.info(message);
+    }
+    else if (level >= DEBUG)
+    {
+      Cache.log.debug(message);
+    }
+    else
+    {
+      Cache.log.trace(message);
+    }
+    return true;
+  }
+
+  public static void trace(String message)
+  {
+    println(TRACE, message);
+  }
+
+  public static void debug(String message)
+  {
+    println(DEBUG, message);
+  }
+
+  public static void info(String message)
+  {
+    println(INFO, message);
+  }
+
+  public static void warn(String message)
+  {
+    println(WARN, message);
+  }
+
+  public static void error(String message)
+  {
+    println(ERROR, message);
+  }
+}
\ No newline at end of file
index 5e20d76..d6d440a 100644 (file)
 package jalview.bin;
 
-import java.awt.Toolkit;
+import java.awt.HeadlessException;
 
 public class HiDPISetting
 {
-  public static final int dpi;
+  private static final int hidpiThreshold = 160;
 
-  public static final int scale;
+  private static final int hidpiMultiThreshold = 240;
 
-  private static final int hidpi = 110;
+  private static final int bigScreenThreshold = 1400;
 
-  private static final String scaleProperty = "sun.java2d.uiScale";
+  public static final String scalePropertyName = "sun.java2d.uiScale";
 
-  private static final boolean isAMac = System.getProperty("os.name")
-          .indexOf("Mac") > -1;
+  private static final boolean isLinux;
 
-  private static final boolean isLinux = System.getProperty("os.name")
-          .toLowerCase().indexOf("linux") > -1;
+  // private static final boolean isAMac;
 
-  private static final boolean isWindows = System.getProperty("os.name")
-          .toLowerCase().indexOf("windows") > -1;
+  // private static final boolean isWindows;
+
+  public static final String setHiDPIPropertyName = "setHiDPI";
+
+  public static final String setHiDPIScalePropertyName = "setHiDPIScale";
+
+  private static boolean setHiDPI = false;
+
+  private static int setHiDPIScale = 0;
+
+  public static int dpi = 0;
+
+  public static int mindimension = 0;
+
+  public static int width = 0;
+
+  public static int scale = 0;
+
+  private static boolean doneInit = false;
+
+  private static boolean allowScalePropertyArg = false;
+
+  private static ScreenInfo screenInfo = new ScreenInfo();
 
   static
   {
-    dpi = Toolkit.getDefaultToolkit().getScreenResolution();
-    scale = dpi / hidpi + 1;
-    if (scale > 1 && isLinux)
+    String system = System.getProperty("os.name") == null ? null
+            : System.getProperty("os.name").toLowerCase();
+    if (system != null)
+    {
+      isLinux = system.indexOf("linux") > -1;
+      // isAMac = system.indexOf("mac") > -1;
+      // isWindows = system.indexOf("windows") > -1;
+    }
+    else
     {
-      System.out.println("DPI detected as " + dpi
-              + ". Scaling factor set to " + scale + ".");
+      isLinux = false;
+      // isAMac = isWindows = false;
     }
   }
 
-  public static String getScalePropertyArg()
+  private static void init()
   {
-    // HiDPI setting. Just looking at Linux to start with. Test with Windows.
-    if (!isLinux)
+    if (doneInit)
     {
-      return null;
+      return;
     }
-    if (scale > 1)
+
+    // get and use command line property values first
+    String setHiDPIProperty = System.getProperty(setHiDPIPropertyName);
+    setHiDPI = setHiDPIProperty != null
+            && setHiDPIProperty.equalsIgnoreCase("true");
+
+    String setHiDPIScaleProperty = System
+            .getProperty(setHiDPIScalePropertyName);
+    if (setHiDPIScaleProperty != null)
+    {
+      try
+      {
+        setHiDPIScale = Integer.parseInt(setHiDPIScaleProperty);
+      } catch (NumberFormatException e)
+      {
+        System.err.println(setHiDPIScalePropertyName + " property give ("
+                + setHiDPIScaleProperty + ") but not parseable as integer");
+      }
+    }
+    if (setHiDPI && setHiDPIScale > 0)
+    {
+      setHiDPIScale(setHiDPIScale);
+      return;
+    }
+
+    // check to see if the scale property has already been set by something else
+    // (e.g. the OS)
+    String existingProperty = System.getProperty(scalePropertyName);
+    if (existingProperty != null)
+    {
+      try
+      {
+        int existingPropertyVal = Integer.parseInt(existingProperty);
+        System.out.println("Existing " + scalePropertyName + " is "
+                + existingPropertyVal);
+        if (existingPropertyVal > 1)
+        {
+          setHiDPIScale(existingPropertyVal);
+          return;
+        }
+      } catch (NumberFormatException e)
+      {
+        System.out.println("Could not convert property " + scalePropertyName
+                + " vale '" + existingProperty + "' to number");
+      }
+    }
+
+    // Try and auto guess a good scale based on reported DPI (not trustworthy)
+    // and screen resolution (more trustworthy)
+
+    // get screen dpi
+    screenInfo = getScreenInfo();
+    try
+    {
+      dpi = screenInfo.getScreenResolution();
+    } catch (HeadlessException e)
+    {
+      System.err.println("Cannot get screen resolution: " + e.getMessage());
+    }
+
+    // try and get screen size height and width
+    try
+    {
+      int height = screenInfo.getScreenHeight();
+      int width = screenInfo.getScreenWidth();
+      // using mindimension in case of portrait screens
+      mindimension = Math.min(height, width);
+    } catch (HeadlessException e)
+    {
+      System.err.println(
+              "Cannot get screen size height and width:" + e.getMessage());
+    }
+
+    // attempt at a formula for scaling based on screen dpi and mindimension.
+    // scale will be an integer >=1. This formula is based on some testing and
+    // guesswork!
+
+    // scale based on reported dpi. if dpi>hidpiThreshold then scale=2+multiples
+    // of hidpiMultiThreshold (else scale=1)
+    // (e.g. dpi of 110 scales 1. dpi of 120 scales 2. dpi of 360 scales 3)
+    int dpiScale = (dpi - hidpiThreshold > 0)
+            ? 2 + ((dpi - hidpiThreshold) / hidpiMultiThreshold)
+            : 1;
+
+    int dimensionScale = 1 + (mindimension / bigScreenThreshold);
+
+    // choose larger of dimensionScale or dpiScale (most likely dimensionScale
+    // as dpiScale often misreported)
+    int autoScale = Math.max(dpiScale, dimensionScale);
+
+    // only make an automatic change if scale is changed and other conditions
+    // (OS is linux) apply, or if setHiDPI has been specified
+    if ((autoScale > 1 && isLinux) || setHiDPI)
+    {
+      setHiDPIScale(autoScale);
+      return;
+    }
+
+    // looks like we're not doing any scaling
+    doneInit = true;
+  }
+
+  public static void setHiDPIScale(int s)
+  {
+    scale = s;
+    allowScalePropertyArg = true;
+    doneInit = true;
+  }
+
+  public static String getScalePropertyArg(int s)
+  {
+    return "-D" + scalePropertyName + "=" + String.valueOf(s);
+  }
+
+  public static String getScalePropertyArg()
+  {
+    init();
+    // HiDPI setting. Just looking at Linux to start with. Test with Windows.
+    return allowScalePropertyArg ? getScalePropertyArg(scale) : null;
+  }
+
+  public static void clear()
+  {
+    setHiDPI = false;
+    setHiDPIScale = 0;
+    dpi = 0;
+    mindimension = 0;
+    width = 0;
+    scale = 0;
+    doneInit = false;
+    allowScalePropertyArg = false;
+  }
+
+  public static void setScreenInfo(ScreenInfo si)
+  {
+    screenInfo = si;
+  }
+
+  public static ScreenInfo getScreenInfo()
+  {
+    if (screenInfo == null)
     {
-      return "-D" + scaleProperty + "=" + scale;
+      screenInfo = new ScreenInfo();
     }
-    return null;
+    return screenInfo;
   }
 }
index c854a74..4c21624 100755 (executable)
@@ -69,6 +69,7 @@ import jalview.io.gff.SequenceOntologyFactory;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.util.ChannelProperties;
+import jalview.util.HttpUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.ws.jws2.Jws2Discoverer;
@@ -95,8 +96,9 @@ public class Jalview
     Platform.getURLCommandArguments();
   }
 
-  // singleton instance of this class
-
+  /*
+   * singleton instance of this class
+   */
   private static Jalview instance;
 
   private Desktop desktop;
@@ -329,8 +331,8 @@ public class Jalview
                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
         }
       }
-
     }
+
     String defs = aparser.getValue("setprop");
     while (defs != null)
     {
@@ -346,6 +348,9 @@ public class Jalview
         {
           Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
         }
+        // DISABLED FOR SECURITY REASONS
+        // TODO: add a property to allow properties to be overriden by cli args
+        // Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
       }
       defs = aparser.getValue("setprop");
     }
@@ -369,91 +374,7 @@ public class Jalview
 
     desktop = null;
 
-    // property laf = "crossplatform", "system", "gtk", "metal" or "mac"
-    // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
-    // try Quaqua/Vaqua.
-    String lafProp = System.getProperty("laf");
-    String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
-    String laf = "none";
-    if (lafProp != null)
-    {
-      laf = lafProp;
-    }
-    else if (lafSetting != null)
-    {
-      laf = lafSetting;
-    }
-    boolean lafSet = false;
-    switch (laf)
-    {
-    case "crossplatform":
-      lafSet = setCrossPlatformLookAndFeel();
-      if (!lafSet)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "system":
-      lafSet = setSystemLookAndFeel();
-      if (!lafSet)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "gtk":
-      lafSet = setGtkLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "metal":
-      lafSet = setMetalLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "nimbus":
-      lafSet = setNimbusLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "quaqua":
-      lafSet = setQuaquaLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "vaqua":
-      lafSet = setVaquaLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "mac":
-      lafSet = setMacLookAndFeel();
-      if (!lafSet)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "none":
-      break;
-    default:
-      System.err.println("Requested laf=" + laf + " not implemented");
-    }
-    if (!lafSet)
-    {
-      setSystemLookAndFeel();
-      if (Platform.isLinux() && !Platform.isJS())
-      {
-        setMetalLookAndFeel();
-      }
-      if (Platform.isAMacAndNotJS())
-      {
-        setMacLookAndFeel();
-      }
-    }
+    setLookAndFeel();
 
     /*
      * configure 'full' SO model if preferences say to, else use the default (full SO)
@@ -467,18 +388,28 @@ public class Jalview
 
     if (!headless)
     {
-
+      Desktop.nosplash = aparser.contains("nosplash");
       desktop = new Desktop();
       desktop.setInBatchMode(true); // indicate we are starting up
 
       try
       {
         JalviewTaskbar.setTaskbar(this);
+      } catch (Exception e)
+      {
+        Cache.log.info("Cannot set Taskbar");
+        Cache.log.error(e.getMessage());
+        // e.printStackTrace();
       } catch (Throwable t)
       {
-        System.out.println("Error setting Taskbar: " + t.getMessage());
+        Cache.log.info("Cannot set Taskbar");
+        Cache.log.error(t.getMessage());
+        // t.printStackTrace();
       }
 
+      // set Proxy settings before all the internet calls
+      Cache.setProxyPropertiesFromPreferences();
+
       desktop.setVisible(true);
 
       if (!Platform.isJS())
@@ -488,7 +419,10 @@ public class Jalview
        * @j2sIgnore
        */
       {
-        desktop.startServiceDiscovery();
+        if (!aparser.contains("nowebservicediscovery"))
+        {
+          desktop.startServiceDiscovery();
+        }
         if (!aparser.contains("nousagestats"))
         {
           startUsageStats(desktop);
@@ -596,8 +530,7 @@ public class Jalview
        * @j2sIgnore
        */
       {
-        if (!file.startsWith("http://") && !file.startsWith("https://"))
-        // BH 2019 added https check for Java
+        if (!HttpUtils.startsWithHttpOrHttps(file))
         {
           if (!(new File(file)).exists())
           {
@@ -861,12 +794,7 @@ public class Jalview
         Cache.removeProperty("STARTUP_FILE");
       }
 
-      protocol = DataSourceType.FILE;
-
-      if (file.indexOf("http:") > -1)
-      {
-        protocol = DataSourceType.URL;
-      }
+      protocol = AppletFormatAdapter.checkProtocol(file);
 
       if (file.endsWith(".jar"))
       {
@@ -914,29 +842,130 @@ public class Jalview
     }
   }
 
-  private static boolean setCrossPlatformLookAndFeel()
+  private static void setLookAndFeel()
   {
-    return setGenericLookAndFeel(false);
+    // property laf = "crossplatform", "system", "gtk", "metal", "nimbus" or
+    // "mac"
+    // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
+    // try Quaqua/Vaqua.
+    String lafProp = System.getProperty("laf");
+    String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
+    String laf = "none";
+    if (lafProp != null)
+    {
+      laf = lafProp;
+    }
+    else if (lafSetting != null)
+    {
+      laf = lafSetting;
+    }
+    boolean lafSet = false;
+    switch (laf)
+    {
+    case "crossplatform":
+      lafSet = setCrossPlatformLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "system":
+      lafSet = setSystemLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "gtk":
+      lafSet = setGtkLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "metal":
+      lafSet = setMetalLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "nimbus":
+      lafSet = setNimbusLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "quaqua":
+      lafSet = setQuaquaLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "vaqua":
+      lafSet = setVaquaLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "mac":
+      lafSet = setMacLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "none":
+      break;
+    default:
+      Cache.log.error("Requested laf=" + laf + " not implemented");
+    }
+    if (!lafSet)
+    {
+      setSystemLookAndFeel();
+      if (Platform.isLinux())
+      {
+        setMetalLookAndFeel();
+      }
+      if (Platform.isMac())
+      {
+        setMacLookAndFeel();
+      }
+    }
   }
 
-  private static boolean setSystemLookAndFeel()
+  private static boolean setCrossPlatformLookAndFeel()
   {
-    return setGenericLookAndFeel(true);
+    boolean set = false;
+    try
+    {
+      UIManager.setLookAndFeel(
+              UIManager.getCrossPlatformLookAndFeelClassName());
+      set = true;
+    } catch (Exception ex)
+    {
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
+    }
+    return set;
   }
 
-  private static boolean setGenericLookAndFeel(boolean system)
+  private static boolean setSystemLookAndFeel()
   {
     boolean set = false;
     try
     {
-      UIManager.setLookAndFeel(
-              system ? UIManager.getSystemLookAndFeelClassName()
-                      : UIManager.getCrossPlatformLookAndFeelClassName());
+      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
       set = true;
     } catch (Exception ex)
     {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -962,8 +991,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
index 77df4c2..b8d31c2 100644 (file)
@@ -160,6 +160,8 @@ public class Launcher
     String scalePropertyArg = HiDPISetting.getScalePropertyArg();
     if (scalePropertyArg != null)
     {
+      System.out.println("Running " + startClass + " with scale setting "
+              + scalePropertyArg);
       command.add(scalePropertyArg);
     }
 
@@ -174,7 +176,8 @@ public class Launcher
               "LAUNCHER COMMAND: " + String.join(" ", builder.command()));
     }
     System.out.println("Running " + startClass + " with "
-            + (memSetting == null ? "no memory setting" : memSetting));
+            + (memSetting == null ? "no memory setting"
+                    : ("memory setting " + memSetting)));
 
     if (Boolean.parseBoolean(System.getProperty("launcherstop", "false")))
     {
index 159374a..5d7f14c 100644 (file)
@@ -41,9 +41,9 @@ public class MemorySetting
 
   private static final long GIGABYTE = 1073741824; // 1GB
 
-  public static final long LEAVE_FREE_MIN_MEMORY = GIGABYTE/2;
+  public static final long LEAVE_FREE_MIN_MEMORY = GIGABYTE / 2;
 
-  public static final long APPLICATION_MIN_MEMORY = GIGABYTE/2;
+  public static final long APPLICATION_MIN_MEMORY = GIGABYTE / 2;
 
   private static final long MAX_HEAPSIZE_GB_DEFAULT = 32;
 
@@ -61,28 +61,27 @@ public class MemorySetting
    * values: jvmmempc - the maximum percentage of total physical memory to
    * allocate, and jvmmemmax - the maximum absolute amount of physical memory to
    * allocate. These can be provided as arguments. If not provided as arguments
-   * (or set as null) system properties will be used instead (if set). The memory
-   * setting returned will be the lower of the two values. If either of the values
-   * are not provided then defaults will be used (jvmmempc=90, jvmmemmax=32GB). If
-   * total physical memory can't be ascertained when jvmmempc was set or neither
-   * jvmmempc nor jvmmemmax were set, then jvmmemmax defaults to a much safer 8GB.
-   * In this case explicitly setting jvmmemmax and not setting jvmmempc can set a
-   * higher memory for Jalview. The calculation also tries to ensure 0.5GB memory
-   * for the OS, but also tries to ensure at least 0.5GB memory for Jalview (which
-   * takes priority over the OS) If there is less then 0.5GB of physical memory
-   * then the total physical memory is used for Jalview.
+   * (or set as null) system properties will be used instead (if set). The
+   * memory setting returned will be the lower of the two values. If either of
+   * the values are not provided then defaults will be used (jvmmempc=90,
+   * jvmmemmax=32GB). If total physical memory can't be ascertained when
+   * jvmmempc was set or neither jvmmempc nor jvmmemmax were set, then jvmmemmax
+   * defaults to a much safer 8GB. In this case explicitly setting jvmmemmax and
+   * not setting jvmmempc can set a higher memory for Jalview. The calculation
+   * also tries to ensure 0.5GB memory for the OS, but also tries to ensure at
+   * least 0.5GB memory for Jalview (which takes priority over the OS) If there
+   * is less then 0.5GB of physical memory then the total physical memory is
+   * used for Jalview.
    * 
    * @param jvmmemmaxarg
-   *                       Maximum value of memory to set. This can be a numeric
-   *                       string optionally followed by "b", "k", "m", "g", "t"
-   *                       (case insensitive) to indicate bytes, kilobytes,
-   *                       megabytes, gigabytes, terabytes respectively. If null a
-   *                       default value of 32G will be used. If null and either
-   *                       physical memory can't be determined then the default is
-   *                       8GB.
+   *          Maximum value of memory to set. This can be a numeric string
+   *          optionally followed by "b", "k", "m", "g", "t" (case insensitive)
+   *          to indicate bytes, kilobytes, megabytes, gigabytes, terabytes
+   *          respectively. If null a default value of 32G will be used. If null
+   *          and either physical memory can't be determined then the default is
+   *          8GB.
    * @param jvmmempcarg
-   *                       Max percentage of physical memory to use. Defaults to
-   *                       "90".
+   *          Max percentage of physical memory to use. Defaults to "90".
    * 
    * @return The amount of memory (in bytes) to allocate to Jalview
    */
@@ -136,9 +135,8 @@ public class MemorySetting
       {
         memmax = MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE;
         System.out.println("MemorySetting Property '"
-                + MAX_HEAPSIZE_PROPERTY_NAME
-                + "' ("
-                + jvmmemmaxarg + "') badly formatted, using default ("
+                + MAX_HEAPSIZE_PROPERTY_NAME + "' (" + jvmmemmaxarg
+                + "') badly formatted, using default ("
                 + MAX_HEAPSIZE_GB_DEFAULT + "g).");
       }
 
@@ -146,11 +144,10 @@ public class MemorySetting
       if (Long.MAX_VALUE / memmax < multiplier)
       {
         memmax = MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE;
-        System.out.println(
-                "MemorySetting Property '" + MAX_HEAPSIZE_PROPERTY_NAME + "' ("
-                        + jvmmemmaxarg
-                        + ") too big, using default ("
-                        + MAX_HEAPSIZE_GB_DEFAULT + "g).");
+        System.out.println("MemorySetting Property '"
+                + MAX_HEAPSIZE_PROPERTY_NAME + "' (" + jvmmemmaxarg
+                + ") too big, using default (" + MAX_HEAPSIZE_GB_DEFAULT
+                + "g).");
       }
       else
       {
@@ -161,11 +158,10 @@ public class MemorySetting
       if (memmax < APPLICATION_MIN_MEMORY)
       {
         memmax = APPLICATION_MIN_MEMORY;
-        System.out.println(
-                "MemorySetting Property '" + MAX_HEAPSIZE_PROPERTY_NAME + "' ("
-                        + jvmmemmaxarg
-                        + ") too small, using minimum ("
-                        + APPLICATION_MIN_MEMORY + ").");
+        System.out.println("MemorySetting Property '"
+                + MAX_HEAPSIZE_PROPERTY_NAME + "' (" + jvmmemmaxarg
+                + ") too small, using minimum (" + APPLICATION_MIN_MEMORY
+                + ").");
       }
 
     }
@@ -196,18 +192,17 @@ public class MemorySetting
         }
         else
         {
-          System.out.println(
-                  "MemorySetting Property '"
-                          + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME
-                          + "' should be in range 1..100. Using default "
-                          + percent + "%");
+          System.out.println("MemorySetting Property '"
+                  + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME
+                  + "' should be in range 1..100. Using default " + percent
+                  + "%");
         }
       }
     } catch (NumberFormatException e)
     {
-      System.out.println(
-              "MemorySetting property '" + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME
-                      + "' (" + jvmmempcarg + ") badly formatted");
+      System.out.println("MemorySetting property '"
+              + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' (" + jvmmempcarg
+              + ") badly formatted");
     }
 
     // catch everything in case of no com.sun.management.OperatingSystemMXBean
@@ -246,9 +241,9 @@ public class MemorySetting
           else
           {
             System.out.println("MemorySetting Property '"
-                    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' (" + jvmmempcarg
-                    + ") too small. Using minimum (" + APPLICATION_MIN_MEMORY
-                    + ").");
+                    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' ("
+                    + jvmmempcarg + ") too small. Using minimum ("
+                    + APPLICATION_MIN_MEMORY + ").");
           }
           mempc = APPLICATION_MIN_MEMORY;
         }
@@ -277,7 +272,12 @@ public class MemorySetting
     // jvmmempc was set OR neither jvmmempc nor jvmmemmax were set), let's cap
     // maxMemLong to 8GB
     if (memoryPercentError && mempc == -1
-            && !(jvmmempcarg == null && jvmmemmaxarg != null) // the same as (jvmmempcarg != null || (jvmmempcarg == null && jvmmemmaxarg
+            && !(jvmmempcarg == null && jvmmemmaxarg != null) // the same as
+                                                              // (jvmmempcarg !=
+                                                              // null ||
+                                                              // (jvmmempcarg ==
+                                                              // null &&
+                                                              // jvmmemmaxarg
                                                               // == null))
             && memmax > NOMEM_MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE)
     {
diff --git a/src/jalview/bin/ScreenInfo.java b/src/jalview/bin/ScreenInfo.java
new file mode 100644 (file)
index 0000000..899bf94
--- /dev/null
@@ -0,0 +1,21 @@
+package jalview.bin;
+
+import java.awt.Toolkit;
+
+public class ScreenInfo
+{
+  public int getScreenResolution()
+  {
+    return Toolkit.getDefaultToolkit().getScreenResolution();
+  }
+
+  public int getScreenHeight()
+  {
+    return (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
+  }
+
+  public int getScreenWidth()
+  {
+    return (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+  }
+}
index 1f3f409..5370437 100644 (file)
@@ -48,6 +48,7 @@ import java.awt.print.PrinterJob;
 import java.beans.PropertyChangeEvent;
 import java.io.File;
 import java.io.FileWriter;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URL;
 import java.util.ArrayList;
@@ -143,6 +144,7 @@ import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemes;
 import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.TCoffeeColourScheme;
+import jalview.util.HttpUtils;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
@@ -345,8 +347,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   void init()
   {
-//       setBackground(Color.white); // BH 2019
-                 
+    // setBackground(Color.white); // BH 2019
+
     if (!Jalview.isHeadlessMode())
     {
       progressBar = new ProgressBar(this.statusPanel, this.statusBar);
@@ -1061,7 +1063,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         Desktop.instance.closeAssociatedWindows();
 
         FileLoader loader = new FileLoader();
-        DataSourceType protocol = fileName.startsWith("http:")
+        DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(fileName)
                 ? DataSourceType.URL
                 : DataSourceType.FILE;
         loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
@@ -1077,9 +1079,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         if (fileObject == null)
         {
 
-          DataSourceType protocol = (fileName.startsWith("http:")
-                  ? DataSourceType.URL
-                  : DataSourceType.FILE);
+          DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(
+                  fileName) ? DataSourceType.URL : DataSourceType.FILE;
           newframe = loader.LoadFileWaitTillLoaded(fileName, protocol,
                   currentFileFormat);
         }
@@ -1130,7 +1131,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void save_actionPerformed(ActionEvent e)
   {
     if (fileName == null || (currentFileFormat == null)
-            || fileName.startsWith("http"))
+            || HttpUtils.startsWithHttpOrHttps(fileName))
     {
       saveAs_actionPerformed();
     }
@@ -1203,11 +1204,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (!lastSaveSuccessful)
     {
-      JvOptionPane.showInternalMessageDialog(this, MessageManager
-              .formatMessage("label.couldnt_save_file", new Object[]
-              { lastFilenameSaved }),
-              MessageManager.getString("label.error_saving_file"),
-              JvOptionPane.WARNING_MESSAGE);
+      if (!Platform.isHeadless())
+      {
+        JvOptionPane.showInternalMessageDialog(this, MessageManager
+                .formatMessage("label.couldnt_save_file", new Object[]
+                { lastFilenameSaved }),
+                MessageManager.getString("label.error_saving_file"),
+                JvOptionPane.WARNING_MESSAGE);
+      }
+      else
+      {
+        Cache.log.error(MessageManager
+                .formatMessage("label.couldnt_save_file", new Object[]
+                { lastFilenameSaved }));
+      }
     }
     else
     {
@@ -1241,15 +1251,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       String shortName = title;
       if (shortName.indexOf(File.separatorChar) > -1)
       {
-        shortName = shortName.substring(
-                shortName.lastIndexOf(File.separatorChar) + 1);
+        shortName = shortName
+                .substring(shortName.lastIndexOf(File.separatorChar) + 1);
       }
-      lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file, shortName);
-      
+      lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file,
+              shortName);
+
       statusBar.setText(MessageManager.formatMessage(
               "label.successfully_saved_to_file_in_format", new Object[]
-              { fileName, format }));
-      
+              { file, format }));
+
       return;
     }
 
@@ -1283,31 +1294,65 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         {
           // create backupfiles object and get new temp filename destination
           boolean doBackup = BackupFiles.getEnabled();
-          BackupFiles backupfiles = doBackup ? new BackupFiles(file) : null;
+          BackupFiles backupfiles = null;
+          if (doBackup)
+          {
+            Cache.log.trace(
+                    "ALIGNFRAME making backupfiles object for " + file);
+            backupfiles = new BackupFiles(file);
+          }
           try
           {
-            String tempFilePath = doBackup ? backupfiles.getTempFilePath() : file;
-                       PrintWriter out = new PrintWriter(
-                    new FileWriter(tempFilePath));
+            String tempFilePath = doBackup ? backupfiles.getTempFilePath()
+                    : file;
+            Cache.log.trace("ALIGNFRAME setting PrintWriter");
+            PrintWriter out = new PrintWriter(new FileWriter(tempFilePath));
+
+            if (backupfiles != null)
+            {
+              Cache.log.trace("ALIGNFRAME about to write to temp file "
+                      + backupfiles.getTempFilePath());
+            }
 
             out.print(output);
+            Cache.log.trace("ALIGNFRAME about to close file");
             out.close();
+            Cache.log.trace("ALIGNFRAME closed file");
             AlignFrame.this.setTitle(file);
             statusBar.setText(MessageManager.formatMessage(
-                  "label.successfully_saved_to_file_in_format", new Object[]
-                  { fileName, format.getName() }));
+                    "label.successfully_saved_to_file_in_format",
+                    new Object[]
+                    { fileName, format.getName() }));
             lastSaveSuccessful = true;
+          } catch (IOException e)
+          {
+            lastSaveSuccessful = false;
+            Cache.log.error(
+                    "ALIGNFRAME Something happened writing the temp file");
+            Cache.log.error(e.getMessage());
+            Cache.log.debug(Cache.getStackTraceString(e));
           } catch (Exception ex)
           {
             lastSaveSuccessful = false;
-            ex.printStackTrace();
+            Cache.log.error(
+                    "ALIGNFRAME Something unexpected happened writing the temp file");
+            Cache.log.error(ex.getMessage());
+            Cache.log.debug(Cache.getStackTraceString(ex));
           }
 
           if (doBackup)
           {
             backupfiles.setWriteSuccess(lastSaveSuccessful);
+            Cache.log.debug("ALIGNFRAME writing temp file was "
+                    + (lastSaveSuccessful ? "" : "NOT ") + "successful");
             // do the backup file roll and rename the temp file to actual file
+            Cache.log.trace(
+                    "ALIGNFRAME about to rollBackupsAndRenameTempFile");
             lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile();
+            Cache.log.debug(
+                    "ALIGNFRAME performed rollBackupsAndRenameTempFile "
+                            + (lastSaveSuccessful ? "" : "un")
+                            + "successfully");
           }
         }
       }
@@ -1489,7 +1534,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     final JalviewFileChooser chooser = new JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new JalviewFileView());
-    String tooltip = MessageManager.getString("label.load_jalview_annotations");
+    String tooltip = MessageManager
+            .getString("label.load_jalview_annotations");
     chooser.setDialogTitle(tooltip);
     chooser.setToolTipText(tooltip);
     chooser.setResponseHandler(0, new Runnable()
@@ -1782,7 +1828,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * Calls AlignmentI.moveSelectedSequencesByOne with current sequence selection or the sequence under cursor in keyboard mode 
+   * Calls AlignmentI.moveSelectedSequencesByOne with current sequence selection
+   * or the sequence under cursor in keyboard mode
    * 
    * @param up
    *          or down (if !up)
@@ -1793,23 +1840,25 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (sg == null)
     {
-      if (viewport.cursorMode) 
+      if (viewport.cursorMode)
       {
         sg = new SequenceGroup();
-        sg.addSequence(viewport.getAlignment()
-                .getSequenceAt(alignPanel.getSeqPanel().seqCanvas.cursorY),false);
-      } else {
+        sg.addSequence(viewport.getAlignment().getSequenceAt(
+                alignPanel.getSeqPanel().seqCanvas.cursorY), false);
+      }
+      else
+      {
         return;
       }
     }
-    
+
     if (sg.getSize() < 1)
     {
-        return;
+      return;
     }
-    
+
     // TODO: JAL-3733 - add an event to the undo buffer for this !
-    
+
     viewport.getAlignment().moveSelectedSequencesByOne(sg,
             viewport.getHiddenRepSequences(), up);
     alignPanel.paintAlignment(true, false);
@@ -2419,56 +2468,61 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    Runnable okAction = new Runnable() 
-    {
-               @Override
-               public void run() 
-               {
-                   SequenceI[] cut = sg.getSequences()
-                           .toArray(new SequenceI[sg.getSize()]);
-
-                   addHistoryItem(new EditCommand(
-                           MessageManager.getString("label.cut_sequences"), Action.CUT,
-                           cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
-                           viewport.getAlignment()));
-
-                   viewport.setSelectionGroup(null);
-                   viewport.sendSelection();
-                   viewport.getAlignment().deleteGroup(sg);
-
-                   viewport.firePropertyChange("alignment", null,
-                           viewport.getAlignment().getSequences());
-                   if (viewport.getAlignment().getHeight() < 1)
-                   {
-                     try
-                     {
-                       AlignFrame.this.setClosed(true);
-                     } catch (Exception ex)
-                     {
-                     }
-                   }
-               }};
+    Runnable okAction = new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        SequenceI[] cut = sg.getSequences()
+                .toArray(new SequenceI[sg.getSize()]);
+
+        addHistoryItem(new EditCommand(
+                MessageManager.getString("label.cut_sequences"), Action.CUT,
+                cut, sg.getStartRes(),
+                sg.getEndRes() - sg.getStartRes() + 1,
+                viewport.getAlignment()));
+
+        viewport.setSelectionGroup(null);
+        viewport.sendSelection();
+        viewport.getAlignment().deleteGroup(sg);
+
+        viewport.firePropertyChange("alignment", null,
+                viewport.getAlignment().getSequences());
+        if (viewport.getAlignment().getHeight() < 1)
+        {
+          try
+          {
+            AlignFrame.this.setClosed(true);
+          } catch (Exception ex)
+          {
+          }
+        }
+      }
+    };
 
     /*
      * If the cut affects all sequences, prompt for confirmation
      */
-    boolean wholeHeight = sg.getSize() == viewport.getAlignment().getHeight();
+    boolean wholeHeight = sg.getSize() == viewport.getAlignment()
+            .getHeight();
     boolean wholeWidth = (((sg.getEndRes() - sg.getStartRes())
             + 1) == viewport.getAlignment().getWidth()) ? true : false;
-       if (wholeHeight && wholeWidth)
-       {
-           JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
-               dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
-           Object[] options = new Object[] { MessageManager.getString("action.ok"),
-                   MessageManager.getString("action.cancel") };
-               dialog.showDialog(MessageManager.getString("warn.delete_all"),
-                   MessageManager.getString("label.delete_all"),
-                   JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
-                   options, options[0]);
-       } else 
-       {
-               okAction.run();
-       }
+    if (wholeHeight && wholeWidth)
+    {
+      JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
+      dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
+      Object[] options = new Object[] {
+          MessageManager.getString("action.ok"),
+          MessageManager.getString("action.cancel") };
+      dialog.showDialog(MessageManager.getString("warn.delete_all"),
+              MessageManager.getString("label.delete_all"),
+              JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
+              options, options[0]);
+    }
+    else
+    {
+      okAction.run();
+    }
   }
 
   /**
@@ -2640,8 +2694,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 column, viewport.getAlignment());
       }
 
-      setStatus(MessageManager
-              .formatMessage("label.removed_columns", new String[]
+      setStatus(MessageManager.formatMessage("label.removed_columns",
+              new String[]
               { Integer.valueOf(trimRegion.getSize()).toString() }));
 
       addHistoryItem(trimRegion);
@@ -2690,8 +2744,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     addHistoryItem(removeGapCols);
 
-    setStatus(MessageManager
-            .formatMessage("label.removed_empty_columns", new Object[]
+    setStatus(MessageManager.formatMessage("label.removed_empty_columns",
+            new Object[]
             { Integer.valueOf(removeGapCols.getSize()).toString() }));
 
     // This is to maintain viewport position on first residue
@@ -2812,8 +2866,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (viewport.getViewName() == null)
     {
-      viewport.setViewName(MessageManager
-              .getString("label.view_name_original"));
+      viewport.setViewName(
+              MessageManager.getString("label.view_name_original"));
     }
 
     /*
@@ -3339,7 +3393,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       JLabel textLabel = new JLabel();
       textLabel.setText(content);
       textLabel.setBackground(Color.WHITE);
-      
+
       pane = new JPanel(new BorderLayout());
       ((JPanel) pane).setOpaque(true);
       pane.setBackground(Color.WHITE);
@@ -3469,8 +3523,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
      * otherwise set the chosen colour scheme (or null for 'None')
      */
     ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name,
-            viewport,
-            viewport.getAlignment(), viewport.getHiddenRepSequences());
+            viewport, viewport.getAlignment(),
+            viewport.getHiddenRepSequences());
     changeColour(cs);
   }
 
@@ -3853,8 +3907,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       Enumeration<String> labels = scoreSorts.keys();
       while (labels.hasMoreElements())
       {
-        addSortByAnnotScoreMenuItem(sortByAnnotScore,
-                labels.nextElement());
+        addSortByAnnotScoreMenuItem(sortByAnnotScore, labels.nextElement());
       }
       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
       scoreSorts.clear();
@@ -4021,7 +4074,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     chooser.setToolTipText(
             MessageManager.getString("label.load_tree_file"));
 
-    chooser.setResponseHandler(0,new Runnable()
+    chooser.setResponseHandler(0, new Runnable()
     {
       @Override
       public void run()
@@ -4189,8 +4242,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               // Add any secondary structure prediction services
               for (int i = 0, j = secstrpr.size(); i < j; i++)
               {
-                final ext.vamsas.ServiceHandle sh = secstrpr
-                        .get(i);
+                final ext.vamsas.ServiceHandle sh = secstrpr.get(i);
                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
                         .getServiceClient(sh);
                 int p = secstrmenu.getItemCount();
@@ -4569,10 +4621,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               if (protocol == DataSourceType.FILE)
               {
                 File fl;
-                if (file instanceof File) {
+                if (file instanceof File)
+                {
                   fl = (File) file;
                   Platform.cacheFileData(fl);
-                } else {
+                }
+                else
+                {
                   fl = new File(fileName);
                 }
                 pdbfn = fl.getName();
@@ -5809,6 +5864,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   private Rectangle lastFeatureSettingsBounds = null;
+
   @Override
   public void setFeatureSettingsGeometry(Rectangle bounds)
   {
index 2e88eeb..b456025 100644 (file)
  */
 package jalview.gui;
 
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
+import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.GraphicsEnvironment;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
 import java.awt.Rectangle;
 import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.awt.event.WindowListener;
@@ -38,12 +43,19 @@ import java.io.PipedOutputStream;
 import java.io.PrintStream;
 
 import javax.swing.JButton;
+import javax.swing.JComboBox;
 import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTextArea;
 
+import org.apache.log4j.Level;
 import org.apache.log4j.SimpleLayout;
 
+import jalview.bin.Cache;
+import jalview.util.MessageManager;
+
 /**
  * Simple Jalview Java Console. Version 1 - allows viewing of console output
  * after desktop is created. Acquired with thanks from RJHM's site
@@ -88,6 +100,10 @@ public class Console extends WindowAdapter
 
   private int MIN_HEIGHT = 250;
 
+  private JComboBox<Level> logLevelCombo = new JComboBox<Level>();
+
+  protected Level startingLogLevel = Level.INFO;
+
   public Console()
   {
     // create all components and add them
@@ -115,17 +131,107 @@ public class Console extends WindowAdapter
     // textArea = cpt.getTextArea();
     textArea = new JTextArea();
     textArea.setEditable(false);
-    JButton button = new JButton(MessageManager.getString("action.clear"));
+    JButton clearButton = new JButton(
+            MessageManager.getString("action.clear"));
+    JButton copyToClipboardButton = new JButton(
+            MessageManager.getString("label.copy_to_clipboard"));
+    copyToClipboardButton.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        copyConsoleTextToClipboard();
+      }
+    });
+    copyToClipboardButton.addMouseListener(new MouseAdapter()
+    {
+      private Color bg = textArea.getBackground();
+
+      private Color fg = textArea.getForeground();
+
+      public void mousePressed(MouseEvent e)
+      {
+        textArea.setBackground(textArea.getSelectionColor());
+        textArea.setForeground(textArea.getSelectedTextColor());
+      }
+
+      public void mouseReleased(MouseEvent e)
+      {
+        textArea.setBackground(bg);
+        textArea.setForeground(fg);
+      }
+
+    });
+    copyToClipboardButton.setToolTipText(
+            MessageManager.getString("label.copy_to_clipboard_tooltip"));
+
+    JLabel logLevelLabel = new JLabel(
+            MessageManager.getString("label.log_level") + ":");
+
+    // logLevelCombo.addItem(Level.ALL);
+    logLevelCombo.addItem(Level.TRACE);
+    logLevelCombo.addItem(Level.DEBUG);
+    logLevelCombo.addItem(Level.INFO);
+    logLevelCombo.addItem(Level.WARN);
+    // logLevelCombo.addItem(Level.ERROR);
+    // logLevelCombo.addItem(Level.FATAL);
+    // logLevelCombo.addItem(Level.OFF);
+    // set startingLogLevel
+    startingLogLevel = Cache.log == null ? Level.INFO
+            : Cache.log.getLevel();
+    setChosenLogLevelCombo();
+    logLevelCombo.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        if (Cache.log != null)
+        {
+          Cache.log.setLevel((Level) logLevelCombo.getSelectedItem());
+        }
+      }
+
+    });
 
     // frame = cpt;
     frame.getContentPane().setLayout(new BorderLayout());
     frame.getContentPane().add(new JScrollPane(textArea),
             BorderLayout.CENTER);
-    frame.getContentPane().add(button, BorderLayout.SOUTH);
+    JPanel southPanel = new JPanel();
+    southPanel.setLayout(new GridBagLayout());
+
+    JPanel logLevelPanel = new JPanel();
+    logLevelPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
+    logLevelPanel.add(logLevelLabel);
+    logLevelPanel.add(logLevelCombo);
+    String logLevelTooltip = MessageManager.formatMessage(
+            "label.log_level_tooltip", startingLogLevel.toString());
+    logLevelLabel.setToolTipText(logLevelTooltip);
+    logLevelCombo.setToolTipText(logLevelTooltip);
+
+    GridBagConstraints gbc = new GridBagConstraints();
+    gbc.gridx = 0;
+    gbc.gridy = 0;
+    gbc.gridwidth = 1;
+    gbc.gridheight = 1;
+    gbc.weightx = 0.1;
+    southPanel.add(logLevelPanel, gbc);
+
+    gbc.gridx++;
+    gbc.weightx = 0.8;
+    gbc.fill = GridBagConstraints.HORIZONTAL;
+    southPanel.add(clearButton, gbc);
+
+    gbc.gridx++;
+    gbc.weightx = 0.1;
+    gbc.fill = GridBagConstraints.NONE;
+    southPanel.add(copyToClipboardButton, gbc);
+
+    southPanel.setVisible(true);
+    frame.getContentPane().add(southPanel, BorderLayout.SOUTH);
     frame.setVisible(visible);
     updateConsole = visible;
     frame.addWindowListener(this);
-    button.addActionListener(this);
+    clearButton.addActionListener(this);
+
     if (redirect)
     {
       redirectStreams();
@@ -151,6 +257,53 @@ public class Console extends WindowAdapter
     textAppender.start();
   }
 
+  private void setChosenLogLevelCombo()
+  {
+    setChosenLogLevelCombo(startingLogLevel);
+  }
+
+  private void setChosenLogLevelCombo(Level setLogLevel)
+  {
+    logLevelCombo.setSelectedItem(setLogLevel);
+    if (!logLevelCombo.getSelectedItem().equals(setLogLevel))
+    {
+      // setLogLevel not (yet) in list
+      if (setLogLevel != null && setLogLevel instanceof Level)
+      {
+        // add new item to list (might be set via .jalview_properties)
+        boolean added = false;
+        for (int i = 0; i < logLevelCombo.getItemCount(); i++)
+        {
+          Level l = (Level) logLevelCombo.getItemAt(i);
+          if (l.isGreaterOrEqual(setLogLevel))
+          {
+            logLevelCombo.insertItemAt(setLogLevel, i);
+            added = true;
+            break;
+          }
+        }
+        if (!added) // lower priority than others or some confusion -- add to
+                    // end of list
+        {
+          logLevelCombo.addItem(setLogLevel);
+        }
+        logLevelCombo.setSelectedItem(setLogLevel);
+      }
+      else
+      {
+        logLevelCombo.setSelectedItem(Level.INFO);
+      }
+    }
+  }
+
+  private void copyConsoleTextToClipboard()
+  {
+    String consoleText = textArea.getText();
+    StringSelection consoleTextSelection = new StringSelection(consoleText);
+    Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
+    cb.setContents(consoleTextSelection, null);
+  }
+
   PipedOutputStream pout = null, perr = null;
 
   public void redirectStreams()
@@ -647,12 +800,19 @@ public class Console extends WindowAdapter
     frame.setVisible(selected);
     if (selected == true)
     {
+      setChosenLogLevelCombo();
       redirectStreams();
       updateConsole = true;
       frame.toFront();
     }
     else
     {
+      // reset log level to what it was before
+      if (Cache.log != null)
+      {
+        Cache.log.setLevel(startingLogLevel);
+      }
+
       unredirectStreams();
       updateConsole = false;
     }
index d49ee40..b38a5a2 100644 (file)
@@ -25,6 +25,7 @@ import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.GridLayout;
 import java.awt.Point;
 import java.awt.Rectangle;
@@ -47,6 +48,7 @@ import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
+import java.awt.geom.AffineTransform;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.File;
@@ -160,6 +162,8 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
 
+  public static boolean nosplash = false;
+
   /**
    * news reader - null if it was never started.
    */
@@ -462,15 +466,17 @@ public class Desktop extends jalview.jbgui.GDesktop
       checkURLLinks();
 
       // Spawn a thread that shows the splashscreen
-
-      SwingUtilities.invokeLater(new Runnable()
+      if (!nosplash)
       {
-        @Override
-        public void run()
+        SwingUtilities.invokeLater(new Runnable()
         {
-          new SplashScreen(true);
-        }
-      });
+          @Override
+          public void run()
+          {
+            new SplashScreen(true);
+          }
+        });
+      }
 
       // Thread off a new instance of the file chooser - this reduces the time
       // it
@@ -535,7 +541,6 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
     });
     desktop.addMouseListener(ma);
-
   }
 
   /**
@@ -1622,7 +1627,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   @Override
   protected void preferences_actionPerformed(ActionEvent e)
   {
-    new Preferences();
+    Preferences.openPreferences();
   }
 
   /**
@@ -2280,6 +2285,9 @@ public class Desktop extends jalview.jbgui.GDesktop
                   10, getHeight() - fm.getHeight());
         }
       }
+
+      // output debug scale message. Important for jalview.bin.HiDPISettingTest2
+      Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
     }
   }
 
@@ -3369,4 +3377,37 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
     return result;
   }
+
+  public static final String debugScaleMessage = "Desktop graphics transform scale=";
+
+  private static boolean debugScaleMessageDone = false;
+
+  public static void debugScaleMessage(Graphics g)
+  {
+    if (debugScaleMessageDone)
+    {
+      return;
+    }
+    // output used by tests to check HiDPI scaling settings in action
+    try
+    {
+      Graphics2D gg = (Graphics2D) g;
+      if (gg != null)
+      {
+        AffineTransform t = gg.getTransform();
+        double scaleX = t.getScaleX();
+        double scaleY = t.getScaleY();
+        Cache.debug(debugScaleMessage + scaleX + " (X)");
+        Cache.debug(debugScaleMessage + scaleY + " (Y)");
+        debugScaleMessageDone = true;
+      }
+      else
+      {
+        Cache.debug("Desktop graphics null");
+      }
+    } catch (Exception e)
+    {
+      Cache.debug(Cache.getStackTraceString(e));
+    }
+  }
 }
index c61c70e..6972657 100755 (executable)
@@ -81,6 +81,10 @@ import jalview.ws.sifts.SiftsSettings;
  * @author $author$
  * @version $Revision$
  */
+/*
+ * for merge with Jalview-JS
+ public class Preferences extends GPreferences implements ApplicationSingletonI
+ */
 public class Preferences extends GPreferences
 {
   public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
@@ -127,6 +131,10 @@ public class Preferences extends GPreferences
 
   private static final int MAX_FONT_SIZE = 30;
 
+  private String previousProxyType;
+
+  private static Preferences INSTANCE = null; // add "final"
+
   /**
    * Holds name and link separated with | character. Sequence ID must be
    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
@@ -186,10 +194,48 @@ public class Preferences extends GPreferences
   private OptionsParam textOpt = new OptionsParam(
           MessageManager.getString("action.text"), "Text");
 
+  // get singleton Preferences instance
+  public static Preferences getInstance()
+  {
+    if (INSTANCE == null || INSTANCE.frame == null
+            || INSTANCE.frame.isClosed())
+    {
+      INSTANCE = new Preferences();
+    }
+    return INSTANCE;
+
+    /*
+     * Replace code with the following for Jalvew-JS
+    Preferences INSTANCE = ApplicationSingletonProvider.getInstance(Preferences.class);
+    if (INSTANCE == null || INSTANCE.frame == null
+            || INSTANCE.frame.isClosed())
+    {
+      ApplicationSingletonProvider.remove(Preferences.class);
+      INSTANCE = ApplicationSingletonProvider.getInstance(Preferences.class);
+    }
+    return INSTANCE;
+    */
+  }
+
+  public static void openPreferences()
+  {
+    openPreferences(0, null);
+  }
+
+  public static void openPreferences(int selectTab, String message)
+  {
+    Preferences p = getInstance();
+    p.selectTab(selectTab);
+    p.setMessage(message);
+    p.frame.show();
+    p.frame.moveToFront();
+    p.frame.grabFocus();
+  }
+
   /**
    * Creates a new Preferences object.
    */
-  public Preferences()
+  private Preferences()
   {
     super();
     frame = new JInternalFrame();
@@ -328,12 +374,10 @@ public class Preferences extends GPreferences
     /*
      * Set overview panel defaults
      */
-    gapColour.setBackground(
-            Cache.getDefaultColour(GAP_COLOUR,
-                    jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP));
-    hiddenColour.setBackground(
-            Cache.getDefaultColour(HIDDEN_COLOUR,
-                    jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN));
+    gapColour.setBackground(Cache.getDefaultColour(GAP_COLOUR,
+            jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP));
+    hiddenColour.setBackground(Cache.getDefaultColour(HIDDEN_COLOUR,
+            jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN));
     useLegacyGap.setSelected(Cache.getDefault(USE_LEGACY_GAP, false));
     gapLabel.setEnabled(!useLegacyGap.isSelected());
     gapColour.setEnabled(!useLegacyGap.isSelected());
@@ -356,7 +400,8 @@ public class Preferences extends GPreferences
      * set choice of structure viewer, and path if saved as a preference;
      * default to Jmol (first choice) if an unexpected value is found
      */
-    String viewerType = Cache.getDefault(STRUCTURE_DISPLAY, ViewerType.JMOL.name());
+    String viewerType = Cache.getDefault(STRUCTURE_DISPLAY,
+            ViewerType.JMOL.name());
     structViewer.setSelectedItem(viewerType);
     String viewerPath = "";
     ViewerType type = null;
@@ -392,10 +437,11 @@ public class Preferences extends GPreferences
       {
         if (validateViewerPath())
         {
-          Cache.setProperty(structViewer.getSelectedItem()
-                  .equals(ViewerType.CHIMERAX.name())
-                  ? CHIMERAX_PATH
-                  : CHIMERA_PATH, structureViewerPath.getText());
+          Cache.setProperty(
+                  structViewer.getSelectedItem().equals(
+                          ViewerType.CHIMERAX.name()) ? CHIMERAX_PATH
+                                  : CHIMERA_PATH,
+                  structureViewerPath.getText());
         }
       }
     });
@@ -536,10 +582,36 @@ public class Preferences extends GPreferences
       }
     }
 
-    useProxy.setSelected(Cache.getDefault("USE_PROXY", false));
-    useProxy_actionPerformed(); // make sure useProxy is correctly initialised
-    proxyServerTB.setText(Cache.getDefault("PROXY_SERVER", ""));
-    proxyPortTB.setText(Cache.getDefault("PROXY_PORT", ""));
+    String proxyTypeString = Cache.getDefault("USE_PROXY", "false");
+    previousProxyType = proxyTypeString;
+    switch (proxyTypeString)
+    {
+    case Cache.PROXYTYPE_NONE:
+      proxyType.setSelected(noProxy.getModel(), true);
+      break;
+    case Cache.PROXYTYPE_SYSTEM:
+      proxyType.setSelected(systemProxy.getModel(), true);
+      break;
+    case Cache.PROXYTYPE_CUSTOM:
+      proxyType.setSelected(customProxy.getModel(), true);
+      break;
+    default:
+      Cache.log.warn(
+              "Incorrect PROXY_TYPE - should be 'none' (clear proxy properties), 'false' (system settings), 'true' (custom settings): "
+                      + proxyTypeString);
+    }
+    proxyServerHttpTB.setText(Cache.getDefault("PROXY_SERVER", ""));
+    proxyPortHttpTB.setText(Cache.getDefault("PROXY_PORT", ""));
+    proxyServerHttpsTB.setText(Cache.getDefault("PROXY_SERVER_HTTPS", ""));
+    proxyPortHttpsTB.setText(Cache.getDefault("PROXY_PORT_HTTPS", ""));
+    proxyAuth.setSelected(Cache.getDefault("PROXY_AUTH", false));
+    proxyAuthUsernameTB
+            .setText(Cache.getDefault("PROXY_AUTH_USERNAME", ""));
+    // we are not storing or retrieving proxy password from .jalview_properties
+    proxyAuthPasswordPB.setText(Cache.proxyAuthPassword == null ? ""
+            : new String(Cache.proxyAuthPassword));
+    setCustomProxyEnabled();
+    applyProxyButtonEnabled(false);
 
     defaultBrowser.setText(Cache.getDefault("DEFAULT_BROWSER", ""));
 
@@ -583,8 +655,7 @@ public class Preferences extends GPreferences
 
     annotations_actionPerformed(null); // update the display of the annotation
                                        // settings
-    
-    
+
     /*
      * Set Backups tab defaults
      */
@@ -604,7 +675,7 @@ public class Preferences extends GPreferences
     comboBox.addItem(promptEachTimeOpt);
     comboBox.addItem(lineArtOpt);
     comboBox.addItem(textOpt);
-    
+
     /*
      * JalviewJS doesn't support Lineart so force it to Text
      */
@@ -638,6 +709,11 @@ public class Preferences extends GPreferences
       return;
     }
 
+    /* 
+     * Set proxy settings first (to be before web services refresh)
+     */
+    saveProxySettings();
+
     /*
      * Save Visual settings
      */
@@ -750,8 +826,7 @@ public class Preferences extends GPreferences
             Boolean.toString(structFromPdb.isSelected()));
     String viewer = structViewer.getSelectedItem().toString();
     String viewerPath = structureViewerPath.getText();
-    Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY,
-            viewer);
+    Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY, viewer);
     if (viewer.equals(ViewerType.CHIMERA.name()))
     {
       Cache.setOrRemove(CHIMERA_PATH, viewerPath);
@@ -781,6 +856,8 @@ public class Preferences extends GPreferences
     /*
      * Save Connections settings
      */
+    // Proxy settings set first (to catch web services)
+
     Cache.setOrRemove("DEFAULT_BROWSER", defaultBrowser.getText());
 
     jalview.util.BrowserLauncher.resetBrowser();
@@ -811,23 +888,6 @@ public class Preferences extends GPreferences
     Cache.applicationProperties.setProperty("DEFAULT_URL",
             sequenceUrlLinks.getPrimaryUrlId());
 
-    Cache.applicationProperties.setProperty("USE_PROXY",
-            Boolean.toString(useProxy.isSelected()));
-
-    Cache.setOrRemove("PROXY_SERVER", proxyServerTB.getText());
-
-    Cache.setOrRemove("PROXY_PORT", proxyPortTB.getText());
-
-    if (useProxy.isSelected())
-    {
-      System.setProperty("http.proxyHost", proxyServerTB.getText());
-      System.setProperty("http.proxyPort", proxyPortTB.getText());
-    }
-    else
-    {
-      System.setProperty("http.proxyHost", "");
-      System.setProperty("http.proxyPort", "");
-    }
     Cache.setProperty("VERSION_CHECK",
             Boolean.toString(versioncheck.isSelected()));
     if (Cache.getProperty("USAGESTATS") != null || usagestats.isSelected())
@@ -897,16 +957,16 @@ public class Preferences extends GPreferences
     Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
             Boolean.toString(enableBackupFiles.isSelected()));
     int preset = getComboIntStringKey(backupfilesPresetsCombo);
-    Cache.applicationProperties.setProperty(BackupFiles.NS + "_PRESET", Integer.toString(preset));
+    Cache.applicationProperties.setProperty(BackupFiles.NS + "_PRESET",
+            Integer.toString(preset));
 
     if (preset == BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM)
     {
       BackupFilesPresetEntry customBFPE = getBackupfilesCurrentEntry();
       BackupFilesPresetEntry.backupfilesPresetEntriesValues.put(
               BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM, customBFPE);
-      Cache.applicationProperties
-              .setProperty(BackupFilesPresetEntry.CUSTOMCONFIG,
-                      customBFPE.toString());
+      Cache.applicationProperties.setProperty(
+              BackupFilesPresetEntry.CUSTOMCONFIG, customBFPE.toString());
     }
 
     BackupFilesPresetEntry savedBFPE = BackupFilesPresetEntry.backupfilesPresetEntriesValues
@@ -924,6 +984,30 @@ public class Preferences extends GPreferences
     }
   }
 
+  public void saveProxySettings()
+  {
+    String newProxyType = customProxy.isSelected() ? Cache.PROXYTYPE_CUSTOM
+            : noProxy.isSelected() ? Cache.PROXYTYPE_NONE
+                    : Cache.PROXYTYPE_SYSTEM;
+    Cache.applicationProperties.setProperty("USE_PROXY", newProxyType);
+    Cache.setOrRemove("PROXY_SERVER", proxyServerHttpTB.getText());
+    Cache.setOrRemove("PROXY_PORT", proxyPortHttpTB.getText());
+    Cache.setOrRemove("PROXY_SERVER_HTTPS", proxyServerHttpsTB.getText());
+    Cache.setOrRemove("PROXY_PORT_HTTPS", proxyPortHttpsTB.getText());
+    Cache.setOrRemove("PROXY_AUTH",
+            Boolean.toString(proxyAuth.isSelected()));
+    Cache.setOrRemove("PROXY_AUTH_USERNAME", proxyAuthUsernameTB.getText());
+    Cache.proxyAuthPassword = proxyAuthPasswordPB.getPassword();
+    Cache.setProxyPropertiesFromPreferences(previousProxyType);
+    if (newProxyType.equals(Cache.PROXYTYPE_CUSTOM)
+            || !newProxyType.equals(previousProxyType))
+    {
+      // force a re-lookup of ws if proxytype is custom or has changed
+      wsPrefs.update++;
+    }
+    previousProxyType = newProxyType;
+  }
+
   /**
    * Do any necessary validation before saving settings. Return focus to the
    * first tab which fails validation.
@@ -1192,8 +1276,7 @@ public class Preferences extends GPreferences
     if (!useLegacyGap.isSelected())
     {
       JalviewColourChooser.showColourChooser(this,
-              MessageManager.getString("label.select_gap_colour"),
-              gap);
+              MessageManager.getString("label.select_gap_colour"), gap);
     }
   }
 
@@ -1201,8 +1284,7 @@ public class Preferences extends GPreferences
   public void hiddenColour_actionPerformed(JPanel hidden)
   {
     JalviewColourChooser.showColourChooser(this,
-            MessageManager.getString("label.select_hidden_colour"),
-            hidden);
+            MessageManager.getString("label.select_hidden_colour"), hidden);
   }
 
   @Override
index 5186a26..e37f77c 100644 (file)
  */
 package jalview.gui;
 
-import jalview.bin.Cache;
-import jalview.jbgui.GWsPreferences;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.Jws2Discoverer;
-import jalview.ws.rest.RestServiceDescription;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -37,13 +31,18 @@ import java.util.List;
 import java.util.Vector;
 
 import javax.swing.JLabel;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.table.AbstractTableModel;
 import javax.swing.table.TableCellRenderer;
 
+import jalview.bin.Cache;
+import jalview.jbgui.GWsPreferences;
+import jalview.util.MessageManager;
+import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.rest.RestServiceDescription;
+
 public class WsPreferences extends GWsPreferences
 {
 
@@ -634,7 +633,9 @@ public class WsPreferences extends GWsPreferences
   /**
    * state counters for ensuring that updates only happen if config has changed.
    */
-  private long update = 0, lastrefresh = 0;
+  protected long update = 0;
+
+  private long lastrefresh = 0;
 
   /*
    * (non-Javadoc)
index 0d5f92b..2b6147f 100644 (file)
  */
 package jalview.io;
 
-import jalview.bin.Cache;
-import jalview.gui.Desktop;
-import jalview.gui.JvOptionPane;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 
+import jalview.bin.Cache;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
 /*
  * BackupFiles used for manipulating (naming rolling/deleting) backup/version files when an alignment or project file is saved.
  * User configurable options are:
@@ -96,6 +100,10 @@ public class BackupFiles
   private static final SimpleDateFormat sdf = new SimpleDateFormat(
           "yyyy-MM-dd HH:mm:ss");
 
+  private static final String newTempFileSuffix = "_newfile";
+
+  private static final String oldTempFileSuffix = "_oldfile_tobedeleted";
+
   public BackupFiles(String filename)
   {
     this(new File(filename));
@@ -107,7 +115,8 @@ public class BackupFiles
   {
     classInit();
     this.file = file;
-    BackupFilesPresetEntry bfpe = BackupFilesPresetEntry.getSavedBackupEntry();
+    BackupFilesPresetEntry bfpe = BackupFilesPresetEntry
+            .getSavedBackupEntry();
     this.suffix = bfpe.suffix;
     this.noMax = bfpe.keepAll;
     this.max = bfpe.rollMax;
@@ -122,30 +131,47 @@ public class BackupFiles
       {
         String tempfilename = file.getName();
         File tempdir = file.getParentFile();
-        temp = File.createTempFile(tempfilename, TEMP_FILE_EXT + "_newfile",
-                tempdir);
+        Cache.trace(
+                "BACKUPFILES [file!=null] attempting to create temp file for "
+                        + tempfilename + " in dir " + tempdir);
+        temp = File.createTempFile(tempfilename,
+                TEMP_FILE_EXT + newTempFileSuffix, tempdir);
+        Cache.debug(
+                "BACKUPFILES using temp file " + temp.getAbsolutePath());
       }
       else
       {
+        Cache.trace(
+                "BACKUPFILES [file==null] attempting to create default temp file "
+                        + DEFAULT_TEMP_FILE + " with extension "
+                        + TEMP_FILE_EXT);
         temp = File.createTempFile(DEFAULT_TEMP_FILE, TEMP_FILE_EXT);
       }
     } catch (IOException e)
     {
-      System.out.println(
-              "Could not create temp file to save into (IOException)");
+      Cache.error("Could not create temp file to save to (IOException)");
+      Cache.error(e.getMessage());
+      Cache.debug(Cache.getStackTraceString(e));
     } catch (Exception e)
     {
-      System.out.println("Exception ctreating temp file for saving");
+      Cache.error("Exception creating temp file for saving");
+      Cache.debug(Cache.getStackTraceString(e));
     }
     this.setTempFile(temp);
   }
 
   public static void classInit()
   {
-    setEnabled(Cache.getDefault(ENABLED, !Platform.isJS()));
+    Cache.initLogger();
+    Cache.trace("BACKUPFILES classInit");
+    boolean e = Cache.getDefault(ENABLED, !Platform.isJS());
+    setEnabled(e);
+    Cache.trace("BACKUPFILES " + (e ? "enabled" : "disabled"));
     BackupFilesPresetEntry bfpe = BackupFilesPresetEntry
             .getSavedBackupEntry();
+    Cache.trace("BACKUPFILES preset scheme " + bfpe.toString());
     setConfirmDelete(bfpe.confirmDelete);
+    Cache.trace("BACKUPFILES confirm delete " + bfpe.confirmDelete);
   }
 
   public static void setEnabled(boolean flag)
@@ -189,9 +215,9 @@ public class BackupFiles
       path = this.getTempFile().getCanonicalPath();
     } catch (IOException e)
     {
-      System.out.println(
-              "IOException when getting Canonical Path of temp file '"
-                      + this.getTempFile().getName() + "'");
+      Cache.error("IOException when getting Canonical Path of temp file '"
+              + this.getTempFile().getName() + "'");
+      Cache.debug(Cache.getStackTraceString(e));
     }
     return path;
   }
@@ -210,7 +236,7 @@ public class BackupFiles
 
   public boolean renameTempFile()
   {
-    return tempFile.renameTo(file);
+    return moveFileToFile(tempFile, file);
   }
 
   // roll the backupfiles
@@ -227,24 +253,34 @@ public class BackupFiles
             || suffix.length() == 0)
     {
       // nothing to do
+      Cache.debug("BACKUPFILES rollBackupFiles nothing to do." + ", "
+              + "filename: " + (file != null ? file.getName() : "null")
+              + ", " + "file exists: " + file.exists() + ", " + "enabled: "
+              + enabled + ", " + "max: " + max + ", " + "suffix: '" + suffix
+              + "'");
       return true;
     }
 
+    Cache.trace("BACKUPFILES rollBackupFiles starting");
+
     String dir = "";
     File dirFile;
     try
     {
       dirFile = file.getParentFile();
       dir = dirFile.getCanonicalPath();
+      Cache.trace("BACKUPFILES dir: " + dir);
     } catch (Exception e)
     {
-      System.out.println(
-              "Could not get canonical path for file '" + file + "'");
+      Cache.error("Could not get canonical path for file '" + file + "'");
+      Cache.error(e.getMessage());
+      Cache.debug(Cache.getStackTraceString(e));
       return false;
     }
     String filename = file.getName();
     String basename = filename;
 
+    Cache.trace("BACKUPFILES filename is " + filename);
     boolean ret = true;
     // Create/move backups up one
 
@@ -256,9 +292,12 @@ public class BackupFiles
     File[] backupFiles = dirFile.listFiles(bff);
     int nextIndexNum = 0;
 
+    Cache.trace("BACKUPFILES backupFiles.length: " + backupFiles.length);
     if (backupFiles.length == 0)
     {
       // No other backup files. Just need to move existing file to backupfile_1
+      Cache.trace(
+              "BACKUPFILES no existing backup files, setting index to 1");
       nextIndexNum = 1;
     }
     else
@@ -271,7 +310,7 @@ public class BackupFiles
       if (reverseOrder)
       {
         // backup style numbering
-
+        Cache.trace("BACKUPFILES rolling files in reverse order");
 
         int tempMax = noMax ? -1 : max;
         // noMax == true means no limits
@@ -288,7 +327,7 @@ public class BackupFiles
             tempMax = i;
           }
         }
-        
+
         File previousFile = null;
         File fileToBeDeleted = null;
         for (int n = tempMax; n > 0; n--)
@@ -303,6 +342,7 @@ public class BackupFiles
             // no "oldest" file to delete
             previousFile = backupfile_n;
             fileToBeDeleted = null;
+            Cache.trace("BACKUPFILES No oldest file to delete");
             continue;
           }
 
@@ -313,19 +353,23 @@ public class BackupFiles
             File replacementFile = backupfile_n;
             long fileToBeDeletedLMT = fileToBeDeleted.lastModified();
             long replacementFileLMT = replacementFile.lastModified();
+            Cache.trace("BACKUPFILES fileToBeDeleted is "
+                    + fileToBeDeleted.getAbsolutePath());
+            Cache.trace("BACKUPFILES replacementFile is "
+                    + backupfile_n.getAbsolutePath());
 
             try
             {
               File oldestTempFile = nextTempFile(fileToBeDeleted.getName(),
                       dirFile);
-              
+
               if (fileToBeDeletedLMT > replacementFileLMT)
               {
                 String fileToBeDeletedLMTString = sdf
                         .format(fileToBeDeletedLMT);
                 String replacementFileLMTString = sdf
                         .format(replacementFileLMT);
-                System.out.println("WARNING! I am set to delete backupfile "
+                Cache.warn("WARNING! I am set to delete backupfile "
                         + fileToBeDeleted.getName()
                         + " has modification time "
                         + fileToBeDeletedLMTString
@@ -336,6 +380,11 @@ public class BackupFiles
 
                 boolean delete = confirmNewerDeleteFile(fileToBeDeleted,
                         replacementFile, true);
+                Cache.trace("BACKUPFILES " + (delete ? "confirmed" : "not")
+                        + " deleting file "
+                        + fileToBeDeleted.getAbsolutePath()
+                        + " which is newer than "
+                        + replacementFile.getAbsolutePath());
 
                 if (delete)
                 {
@@ -344,21 +393,27 @@ public class BackupFiles
                 }
                 else
                 {
-                  fileToBeDeleted.renameTo(oldestTempFile);
+                  Cache.debug("BACKUPFILES moving "
+                          + fileToBeDeleted.getAbsolutePath() + " to "
+                          + oldestTempFile.getAbsolutePath());
+                  moveFileToFile(fileToBeDeleted, oldestTempFile);
                 }
               }
               else
               {
-                fileToBeDeleted.renameTo(oldestTempFile);
+                Cache.debug("BACKUPFILES going to move "
+                        + fileToBeDeleted.getAbsolutePath() + " to "
+                        + oldestTempFile.getAbsolutePath());
+                moveFileToFile(fileToBeDeleted, oldestTempFile);
                 addDeleteFile(oldestTempFile);
               }
 
             } catch (Exception e)
             {
-              System.out.println(
+              Cache.error(
                       "Error occurred, probably making new temp file for '"
                               + fileToBeDeleted.getName() + "'");
-              e.printStackTrace();
+              Cache.error(Cache.getStackTraceString(e));
             }
 
             // reset
@@ -373,7 +428,9 @@ public class BackupFiles
           {
             if (previousFile != null)
             {
-              ret = ret && backupfile_n.renameTo(previousFile);
+              // using boolean '&' instead of '&&' as don't want moveFileToFile
+              // attempt to be conditional (short-circuit)
+              ret = ret & moveFileToFile(backupfile_n, previousFile);
             }
           }
 
@@ -383,19 +440,37 @@ public class BackupFiles
         // index to use for the latest backup
         nextIndexNum = 1;
       }
-      else
+      else // not reverse numbering
       {
         // version style numbering (with earliest file deletion if max files
         // reached)
 
         bfTreeMap.values().toArray(backupFiles);
+        StringBuilder bfsb = new StringBuilder();
+        for (int i = 0; i < backupFiles.length; i++)
+        {
+          if (bfsb.length() > 0)
+          {
+            bfsb.append(", ");
+          }
+          bfsb.append(backupFiles[i].getName());
+        }
+        Cache.trace("BACKUPFILES backupFiles: " + bfsb.toString());
 
         // noMax == true means keep all backup files
         if ((!noMax) && bfTreeMap.size() >= max)
         {
+          Cache.trace("BACKUPFILES noMax: " + noMax + ", " + "max: " + max
+                  + ", " + "bfTreeMap.size(): " + bfTreeMap.size());
           // need to delete some files to keep number of backups to designated
-          // max
-          int numToDelete = bfTreeMap.size() - max + 1;
+          // max.
+          // Note that if the suffix is not numbered then do not delete any
+          // backup files later or we'll delete the new backup file (there can
+          // be only one).
+          int numToDelete = suffix.indexOf(NUM_PLACEHOLDER) > -1
+                  ? bfTreeMap.size() - max + 1
+                  : 0;
+          Cache.trace("BACKUPFILES numToDelete: " + numToDelete);
           // the "replacement" file is the latest backup file being kept (it's
           // not replacing though)
           File replacementFile = numToDelete < backupFiles.length
@@ -408,6 +483,8 @@ public class BackupFiles
             File fileToBeDeleted = backupFiles[i];
             boolean delete = true;
 
+            Cache.trace("BACKUPFILES fileToBeDeleted: " + fileToBeDeleted);
+
             boolean newer = false;
             if (replacementFile != null)
             {
@@ -422,14 +499,13 @@ public class BackupFiles
                 String replacementFileLMTString = sdf
                         .format(replacementFileLMT);
 
-                System.out
-                        .println("WARNING! I am set to delete backupfile '"
-                                + fileToBeDeleted.getName()
-                                + "' has modification time "
+                Cache.warn("WARNING! I am set to delete backupfile '"
+                        + fileToBeDeleted.getName()
+                        + "' has modification time "
                         + fileToBeDeletedLMTString
-                                + " which is newer than the oldest backupfile being kept '"
+                        + " which is newer than the oldest backupfile being kept '"
                         + replacementFile.getName()
-                                + "' with modification time "
+                        + "' with modification time "
                         + replacementFileLMTString);
 
                 delete = confirmNewerDeleteFile(fileToBeDeleted,
@@ -438,17 +514,23 @@ public class BackupFiles
                 {
                   // User has confirmed delete -- no need to add it to the list
                   fileToBeDeleted.delete();
+                  Cache.debug("BACKUPFILES deleting fileToBeDeleted: "
+                          + fileToBeDeleted);
                   delete = false;
                 }
                 else
                 {
                   // keeping file, nothing to do!
+                  Cache.debug("BACKUPFILES keeping fileToBeDeleted: "
+                          + fileToBeDeleted);
                 }
               }
             }
             if (delete)
             {
               addDeleteFile(fileToBeDeleted);
+              Cache.debug("BACKUPFILES addDeleteFile(fileToBeDeleted): "
+                      + fileToBeDeleted);
             }
 
           }
@@ -463,10 +545,16 @@ public class BackupFiles
     String latestBackupFilename = dir + File.separatorChar
             + BackupFilenameParts.getBackupFilename(nextIndexNum, basename,
                     suffix, digits);
-    ret |= file.renameTo(new File(latestBackupFilename));
-
+    Cache.trace("BACKUPFILES Moving old file [" + file
+            + "] to latestBackupFilename [" + latestBackupFilename + "]");
+    // using boolean '&' instead of '&&' as don't want moveFileToFile attempt to
+    // be conditional (short-circuit)
+    ret = ret & moveFileToFile(file, new File(latestBackupFilename));
+    Cache.debug("BACKUPFILES moving " + file + " to " + latestBackupFilename
+            + " was " + (ret ? "" : "NOT ") + "successful");
     if (tidyUp)
     {
+      Cache.debug("BACKUPFILES tidying up files");
       tidyUpFiles();
     }
 
@@ -522,7 +610,7 @@ public class BackupFiles
         saveFile = nextTempFile(ftbd.getName(), ftbd.getParentFile());
       } catch (Exception e)
       {
-        System.out.println(
+        Cache.error(
                 "Error when confirming to keep backup file newer than other backup files.");
         e.printStackTrace();
       }
@@ -530,19 +618,27 @@ public class BackupFiles
               "label.newerdelete_replacement_line", new String[]
               { ftbd.getName(), rf.getName(), ftbdLMT, rfLMT, ftbdSize,
                   rfSize }));
+      // "Backup file\n''{0}''\t(modified {2}, size {4})\nis to be deleted and
+      // replaced by apparently older file \n''{1}''\t(modified {3}, size
+      // {5}).""
       messageSB.append("\n\n");
       messageSB.append(MessageManager.formatMessage(
               "label.confirm_deletion_or_rename", new String[]
               { ftbd.getName(), saveFile.getName() }));
+      // "Confirm deletion of ''{0}'' or rename to ''{1}''?"
       String[] options = new String[] {
           MessageManager.getString("label.delete"),
           MessageManager.getString("label.rename") };
 
-      confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop,
-              messageSB.toString(),
-              MessageManager.getString("label.backupfiles_confirm_delete"),
-              JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
-              null, options, options[0]);
+      confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION
+              : JvOptionPane.showOptionDialog(Desktop.desktop,
+                      messageSB.toString(),
+                      MessageManager.getString(
+                              "label.backupfiles_confirm_delete"),
+                      // "Confirm delete"
+                      JvOptionPane.YES_NO_OPTION,
+                      JvOptionPane.WARNING_MESSAGE, null, options,
+                      options[0]);
     }
     else
     {
@@ -550,22 +646,29 @@ public class BackupFiles
               .formatMessage("label.newerdelete_line", new String[]
               { ftbd.getName(), rf.getName(), ftbdLMT, rfLMT, ftbdSize,
                   rfSize }));
+      // "Backup file\n''{0}''\t(modified {2}, size {4})\nis to be deleted but
+      // is newer than the oldest remaining backup file \n''{1}''\t(modified
+      // {3}, size {5})."
       messageSB.append("\n\n");
       messageSB.append(MessageManager
               .formatMessage("label.confirm_deletion", new String[]
               { ftbd.getName() }));
+      // "Confirm deletion of ''{0}''?"
       String[] options = new String[] {
           MessageManager.getString("label.delete"),
           MessageManager.getString("label.keep") };
 
-      confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop,
-              messageSB.toString(),
-              MessageManager.getString("label.backupfiles_confirm_delete"),
-              JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
-              null, options, options[0]);
+      confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION
+              : JvOptionPane.showOptionDialog(Desktop.desktop,
+                      messageSB.toString(),
+                      MessageManager.getString(
+                              "label.backupfiles_confirm_delete"),
+                      // "Confirm delete"
+                      JvOptionPane.YES_NO_OPTION,
+                      JvOptionPane.WARNING_MESSAGE, null, options,
+                      options[0]);
     }
 
-
     // return should be TRUE if file is to be deleted
     return (confirmButton == JvOptionPane.YES_OPTION);
   }
@@ -581,6 +684,8 @@ public class BackupFiles
         messageSB = new StringBuilder();
         messageSB.append(MessageManager
                 .getString("label.backupfiles_confirm_delete_old_files"));
+        // "Delete the following older backup files? (see the Backups tab in
+        // Preferences for more options)"
         for (int i = 0; i < deleteFiles.size(); i++)
         {
           File df = deleteFiles.get(i);
@@ -591,13 +696,17 @@ public class BackupFiles
                   new String[]
                   { sdf.format(df.lastModified()),
                       Long.toString(df.length()) }));
+          // "(modified {0}, size {1})"
         }
 
-        int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop,
-                messageSB.toString(),
-                MessageManager
-                        .getString("label.backupfiles_confirm_delete"),
-                JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE);
+        int confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION
+                : JvOptionPane.showConfirmDialog(Desktop.desktop,
+                        messageSB.toString(),
+                        MessageManager.getString(
+                                "label.backupfiles_confirm_delete"),
+                        // "Confirm delete"
+                        JvOptionPane.YES_NO_OPTION,
+                        JvOptionPane.WARNING_MESSAGE);
 
         doDelete = (confirmButton == JvOptionPane.YES_OPTION);
       }
@@ -611,8 +720,10 @@ public class BackupFiles
         for (int i = 0; i < deleteFiles.size(); i++)
         {
           File fileToDelete = deleteFiles.get(i);
+          Cache.trace("BACKUPFILES about to delete fileToDelete:"
+                  + fileToDelete);
           fileToDelete.delete();
-          System.out.println("DELETING '" + fileToDelete.getName() + "'");
+          Cache.warn("deleted '" + fileToDelete.getName() + "'");
         }
       }
 
@@ -622,8 +733,7 @@ public class BackupFiles
   }
 
   private TreeMap<Integer, File> sortBackupFilesAsTreeMap(
-          File[] backupFiles,
-          String basename)
+          File[] backupFiles, String basename)
   {
     // sort the backup files (based on integer found in the suffix) using a
     // precomputed Hashmap for speed
@@ -648,7 +758,7 @@ public class BackupFiles
     boolean rename = false;
     if (write)
     {
-      roll = this.rollBackupFiles(false);
+      roll = this.rollBackupFiles(false); // tidyUpFiles at the end
       rename = this.renameTempFile();
     }
 
@@ -662,7 +772,9 @@ public class BackupFiles
     if (!okay)
     {
       StringBuilder messageSB = new StringBuilder();
-      messageSB.append(MessageManager.getString( "label.backupfiles_confirm_save_file_backupfiles_roll_wrong"));
+      messageSB.append(MessageManager.getString(
+              "label.backupfiles_confirm_save_file_backupfiles_roll_wrong"));
+      // "Something possibly went wrong with the backups of this file."
       if (rename)
       {
         if (messageSB.length() > 0)
@@ -671,6 +783,7 @@ public class BackupFiles
         }
         messageSB.append(MessageManager.getString(
                 "label.backupfiles_confirm_save_new_saved_file_ok"));
+        // "The new saved file seems okay."
       }
       else
       {
@@ -680,13 +793,22 @@ public class BackupFiles
         }
         messageSB.append(MessageManager.getString(
                 "label.backupfiles_confirm_save_new_saved_file_not_ok"));
+        // "The new saved file might not be okay."
       }
-
-      int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop,
-              messageSB.toString(),
-              MessageManager
-                      .getString("label.backupfiles_confirm_save_file"),
-              JvOptionPane.OK_OPTION, JvOptionPane.WARNING_MESSAGE);
+      if (messageSB.length() > 0)
+      {
+        messageSB.append("\n");
+      }
+      messageSB
+              .append(MessageManager.getString("label.continue_operation"));
+
+      int confirmButton = Platform.isHeadless() ? JvOptionPane.OK_OPTION
+              : JvOptionPane.showConfirmDialog(Desktop.desktop,
+                      messageSB.toString(),
+                      MessageManager.getString(
+                              "label.backupfiles_confirm_save_file"),
+                      // "Confirm save file"
+                      JvOptionPane.OK_OPTION, JvOptionPane.WARNING_MESSAGE);
       okay = confirmButton == JvOptionPane.OK_OPTION;
     }
     if (okay)
@@ -710,8 +832,7 @@ public class BackupFiles
       dirFile = file.getParentFile();
     } catch (Exception e)
     {
-      System.out.println(
-              "Could not get canonical path for file '" + file + "'");
+      Cache.error("Could not get canonical path for file '" + file + "'");
       return new TreeMap<>();
     }
 
@@ -752,13 +873,49 @@ public class BackupFiles
     int pos = deleteFiles.indexOf(fileToBeDeleted);
     if (pos > -1)
     {
+      Cache.debug("BACKUPFILES not adding file "
+              + fileToBeDeleted.getAbsolutePath()
+              + " to the delete list (already at index" + pos + ")");
       return true;
     }
     else
     {
+      Cache.debug("BACKUPFILES adding file "
+              + fileToBeDeleted.getAbsolutePath() + " to the delete list");
       deleteFiles.add(fileToBeDeleted);
     }
     return ret;
   }
 
+  public static boolean moveFileToFile(File oldFile, File newFile)
+  {
+    Cache.initLogger();
+    boolean ret = false;
+    Path oldPath = Paths.get(oldFile.getAbsolutePath());
+    Path newPath = Paths.get(newFile.getAbsolutePath());
+    try
+    {
+      // delete destination file - not usually necessary but Just In Case...
+      Cache.trace("BACKUPFILES deleting " + newFile.getAbsolutePath());
+      newFile.delete();
+      Cache.trace("BACKUPFILES moving " + oldFile.getAbsolutePath() + " to "
+              + newFile.getAbsolutePath());
+      Files.move(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING);
+      ret = true;
+      Cache.trace("BACKUPFILES move seems to have succeeded");
+    } catch (IOException e)
+    {
+      Cache.warn("Could not move file '" + oldPath.toString() + "' to '"
+              + newPath.toString() + "'");
+      Cache.error(e.getMessage());
+      Cache.debug(Cache.getStackTraceString(e));
+      ret = false;
+    } catch (Exception e)
+    {
+      Cache.error(e.getMessage());
+      Cache.debug(Cache.getStackTraceString(e));
+      ret = false;
+    }
+    return ret;
+  }
 }
index 69130d0..ff8a5e6 100644 (file)
  */
 package jalview.io;
 
-import jalview.bin.Cache;
-import jalview.util.MessageManager;
-
 import java.util.HashMap;
 import java.util.Map;
 import java.util.StringTokenizer;
 
+import jalview.bin.Cache;
+import jalview.util.MessageManager;
+
 public class BackupFilesPresetEntry
 {
 
index ae6727a..38a4732 100755 (executable)
@@ -51,6 +51,7 @@ import javax.swing.JComboBox;
 import javax.swing.JFileChooser;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
+import javax.swing.JPasswordField;
 import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
 import javax.swing.JSpinner;
@@ -68,6 +69,8 @@ import javax.swing.border.EtchedBorder;
 import javax.swing.border.TitledBorder;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import javax.swing.table.TableCellEditor;
 import javax.swing.table.TableCellRenderer;
 
@@ -217,6 +220,8 @@ public class GPreferences extends JPanel
   /*
    * Connections tab components
    */
+  protected JPanel connectTab;
+
   protected JTable linkUrlTable = new JTable();
 
   protected JButton editLink = new JButton();
@@ -229,17 +234,49 @@ public class GPreferences extends JPanel
 
   protected JButton userOnly = new JButton();
 
+  protected JLabel httpLabel = new JLabel();
+
+  protected JLabel httpsLabel = new JLabel();
+
   protected JLabel portLabel = new JLabel();
 
   protected JLabel serverLabel = new JLabel();
 
-  protected JTextField proxyServerTB = new JTextField();
+  protected JLabel portLabel2 = new JLabel();
+
+  protected JLabel serverLabel2 = new JLabel();
+
+  protected JLabel proxyAuthUsernameLabel = new JLabel();
+
+  protected JLabel proxyAuthPasswordLabel = new JLabel();
+
+  protected JLabel passwordNotStoredLabel = new JLabel();
+
+  protected JTextField proxyServerHttpTB = new JTextField();
+
+  protected JTextField proxyPortHttpTB = new JTextField();
 
-  protected JTextField proxyPortTB = new JTextField();
+  protected JTextField proxyServerHttpsTB = new JTextField();
+
+  protected JTextField proxyPortHttpsTB = new JTextField();
+
+  protected JCheckBox proxyAuth = new JCheckBox();
+
+  protected JTextField proxyAuthUsernameTB = new JTextField();
+
+  protected JPasswordField proxyAuthPasswordPB = new JPasswordField();
 
   protected JTextField defaultBrowser = new JTextField();
 
-  protected JCheckBox useProxy = new JCheckBox();
+  protected ButtonGroup proxyType = new ButtonGroup();
+
+  protected JRadioButton noProxy = new JRadioButton();
+
+  protected JRadioButton systemProxy = new JRadioButton();
+
+  protected JRadioButton customProxy = new JRadioButton();
+
+  protected JButton applyProxyButton = new JButton();
 
   protected JCheckBox usagestats = new JCheckBox();
 
@@ -340,6 +377,10 @@ public class GPreferences extends JPanel
 
   protected JTextArea backupfilesExampleLabel = new JTextArea();
 
+  private final JTabbedPane tabbedPane = new JTabbedPane();
+
+  private JLabel messageLabel = new JLabel("", JLabel.CENTER);
+
   /**
    * Creates a new GPreferences object.
    */
@@ -361,8 +402,12 @@ public class GPreferences extends JPanel
    */
   private void jbInit() throws Exception
   {
-    final JTabbedPane tabbedPane = new JTabbedPane();
+    // final JTabbedPane tabbedPane = new JTabbedPane();
     this.setLayout(new BorderLayout());
+
+    // message label at top
+    this.add(messageLabel, BorderLayout.NORTH);
+
     JPanel okCancelPanel = initOkCancelPanel();
     this.add(tabbedPane, BorderLayout.CENTER);
     this.add(okCancelPanel, BorderLayout.SOUTH);
@@ -382,11 +427,11 @@ public class GPreferences extends JPanel
     tabbedPane.add(initConnectionsTab(),
             MessageManager.getString("label.connections"));
 
-       if (!Platform.isJS()) 
-       {
-         tabbedPane.add(initBackupsTab(), 
-                       MessageManager.getString("label.backups"));
-       }
+    if (!Platform.isJS())
+    {
+      tabbedPane.add(initBackupsTab(),
+              MessageManager.getString("label.backups"));
+    }
 
     tabbedPane.add(initLinksTab(),
             MessageManager.getString("label.urllinks"));
@@ -409,6 +454,7 @@ public class GPreferences extends JPanel
     /*
      * Handler to validate a tab before leaving it - currently only for
      * Structure.
+     * Adding a clearMessage() so messages are cleared when changing tabs.
      */
     tabbedPane.addChangeListener(new ChangeListener()
     {
@@ -427,11 +473,57 @@ public class GPreferences extends JPanel
           }
         }
         lastTab = tabbedPane.getSelectedComponent();
+
+        clearMessage();
       }
 
     });
   }
 
+  public void setMessage(String message)
+  {
+    if (message != null)
+    {
+      messageLabel.setText(message);
+      messageLabel.setFont(LABEL_FONT_BOLD);
+      messageLabel.setForeground(Color.RED.darker());
+      messageLabel.revalidate();
+      messageLabel.repaint();
+    }
+    // note message not cleared if message is null. call clearMessage()
+    // directly.
+    this.revalidate();
+    this.repaint();
+  }
+
+  public void clearMessage()
+  {
+    // only repaint if message exists
+    if (messageLabel.getText() != null
+            && messageLabel.getText().length() > 0)
+    {
+      messageLabel.setText("");
+      messageLabel.revalidate();
+      messageLabel.repaint();
+      this.revalidate();
+      this.repaint();
+    }
+  }
+
+  public final static int CONNECTIONS_TAB = 5;
+
+  public void selectTab(int selectTab)
+  {
+    // select a given tab - currently only for Connections
+    switch (selectTab)
+    {
+    case CONNECTIONS_TAB:
+      tabbedPane.setSelectedComponent(connectTab);
+      break;
+    default:
+    }
+  }
+
   /**
    * Initialises the Editing tabbed panel.
    * 
@@ -609,7 +701,7 @@ public class GPreferences extends JPanel
    */
   private JPanel initConnectionsTab()
   {
-    JPanel connectTab = new JPanel();
+    connectTab = new JPanel();
     connectTab.setLayout(new GridBagLayout());
 
     // Label for browser text box
@@ -880,63 +972,355 @@ public class GPreferences extends JPanel
   private JPanel initConnTabProxyPanel()
   {
     // Label for server text box
-    serverLabel.setText(MessageManager.getString("label.address"));
+    serverLabel.setText(MessageManager.getString("label.host") + ": ");
     serverLabel.setHorizontalAlignment(SwingConstants.RIGHT);
     serverLabel.setFont(LABEL_FONT);
+    serverLabel2.setText(MessageManager.getString("label.host") + ": ");
+    serverLabel2.setHorizontalAlignment(SwingConstants.RIGHT);
+    serverLabel2.setFont(LABEL_FONT);
 
     // Proxy server and port text boxes
-    proxyServerTB.setFont(LABEL_FONT);
-    proxyPortTB.setFont(LABEL_FONT);
+    proxyServerHttpTB.setFont(LABEL_FONT);
+    proxyServerHttpTB.setColumns(40);
+    proxyPortHttpTB.setFont(LABEL_FONT);
+    proxyPortHttpTB.setColumns(4);
+    proxyServerHttpsTB.setFont(LABEL_FONT);
+    proxyServerHttpsTB.setColumns(40);
+    proxyPortHttpsTB.setFont(LABEL_FONT);
+    proxyPortHttpsTB.setColumns(4);
+    proxyAuthUsernameTB.setFont(LABEL_FONT);
+    proxyAuthUsernameTB.setColumns(30);
+
+    // check for any change to enable applyProxyButton
+    DocumentListener d = new DocumentListener()
+    {
+      @Override
+      public void changedUpdate(DocumentEvent e)
+      {
+        applyProxyButtonEnabled(true);
+      }
+
+      @Override
+      public void insertUpdate(DocumentEvent e)
+      {
+        applyProxyButtonEnabled(true);
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e)
+      {
+        applyProxyButtonEnabled(true);
+      }
+    };
+    proxyServerHttpTB.getDocument().addDocumentListener(d);
+    proxyPortHttpTB.getDocument().addDocumentListener(d);
+    proxyServerHttpsTB.getDocument().addDocumentListener(d);
+    proxyPortHttpsTB.getDocument().addDocumentListener(d);
+    proxyAuthUsernameTB.getDocument().addDocumentListener(d);
+    proxyAuthPasswordPB.setFont(LABEL_FONT);
+    proxyAuthPasswordPB.setColumns(30);
+    proxyAuthPasswordPB.getDocument()
+            .addDocumentListener(new DocumentListener()
+            {
+              @Override
+              public void changedUpdate(DocumentEvent e)
+              {
+                proxyAuthPasswordCheckHighlight(true);
+                applyProxyButtonEnabled(true);
+              }
+
+              @Override
+              public void insertUpdate(DocumentEvent e)
+              {
+                proxyAuthPasswordCheckHighlight(true);
+                applyProxyButtonEnabled(true);
+              }
+
+              @Override
+              public void removeUpdate(DocumentEvent e)
+              {
+                proxyAuthPasswordCheckHighlight(true);
+                applyProxyButtonEnabled(true);
+              }
+
+            });
 
     // Label for Port text box
     portLabel.setFont(LABEL_FONT);
     portLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-    portLabel.setText(MessageManager.getString("label.port"));
-
-    // Use proxy server checkbox
-    useProxy.setFont(LABEL_FONT);
-    useProxy.setHorizontalAlignment(SwingConstants.RIGHT);
-    useProxy.setHorizontalTextPosition(SwingConstants.LEADING);
-    useProxy.setText(MessageManager.getString("label.use_proxy_server"));
-    useProxy.addActionListener(new ActionListener()
+    portLabel.setText(MessageManager.getString("label.port") + ": ");
+    portLabel2.setFont(LABEL_FONT);
+    portLabel2.setHorizontalAlignment(SwingConstants.RIGHT);
+    portLabel2.setText(MessageManager.getString("label.port") + ": ");
+
+    httpLabel.setText("HTTP");
+    httpLabel.setFont(LABEL_FONT_BOLD);
+    httpLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    httpsLabel.setText("HTTPS");
+    httpsLabel.setFont(LABEL_FONT_BOLD);
+    httpsLabel.setHorizontalAlignment(SwingConstants.LEFT);
+
+    proxyAuthUsernameLabel
+            .setText(MessageManager.getString("label.username") + ": ");
+    proxyAuthUsernameLabel.setFont(LABEL_FONT);
+    proxyAuthUsernameLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    proxyAuthPasswordLabel
+            .setText(MessageManager.getString("label.password") + ": ");
+    proxyAuthPasswordLabel.setFont(LABEL_FONT);
+    proxyAuthPasswordLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    passwordNotStoredLabel.setText(
+            "(" + MessageManager.getString("label.not_stored") + ")");
+    passwordNotStoredLabel.setFont(LABEL_FONT_ITALIC);
+    passwordNotStoredLabel.setHorizontalAlignment(SwingConstants.LEFT);
+
+    // Proxy type radio buttons
+    noProxy.setFont(LABEL_FONT);
+    noProxy.setHorizontalAlignment(SwingConstants.LEFT);
+    noProxy.setText(MessageManager.getString("label.no_proxy"));
+    systemProxy.setFont(LABEL_FONT);
+    systemProxy.setHorizontalAlignment(SwingConstants.LEFT);
+    systemProxy.setText(MessageManager.formatMessage("label.system_proxy",
+            displayUserHostPort(Cache.startupProxyProperties[4],
+                    Cache.startupProxyProperties[0],
+                    Cache.startupProxyProperties[1]),
+            displayUserHostPort(Cache.startupProxyProperties[6],
+                    Cache.startupProxyProperties[2],
+                    Cache.startupProxyProperties[3])));
+    customProxy.setFont(LABEL_FONT);
+    customProxy.setHorizontalAlignment(SwingConstants.LEFT);
+    customProxy.setText(
+            MessageManager.getString("label.use_proxy_server") + ":");
+    ActionListener al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        proxyType_actionPerformed();
+      }
+    };
+    noProxy.addActionListener(al);
+    systemProxy.addActionListener(al);
+    customProxy.addActionListener(al);
+    proxyType.add(noProxy);
+    proxyType.add(systemProxy);
+    proxyType.add(customProxy);
+
+    proxyAuth.setFont(LABEL_FONT);
+    proxyAuth.setHorizontalAlignment(SwingConstants.LEFT);
+    proxyAuth.setText(MessageManager.getString("label.auth_required"));
+    proxyAuth.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        useProxy_actionPerformed();
+        proxyAuth_actionPerformed();
       }
     });
 
+    setCustomProxyEnabled();
+
     // Make proxy server panel
     JPanel proxyPanel = new JPanel();
     TitledBorder titledBorder1 = new TitledBorder(
-            MessageManager.getString("label.proxy_server"));
+            MessageManager.getString("label.proxy_servers"));
     proxyPanel.setBorder(titledBorder1);
     proxyPanel.setLayout(new GridBagLayout());
-    proxyPanel.add(serverLabel,
-            new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.NONE,
-                    new Insets(0, 2, 2, 0), 5, 0));
-    proxyPanel.add(portLabel,
-            new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.NONE,
-                    new Insets(0, 0, 2, 0), 11, 0));
-    proxyPanel.add(useProxy,
-            new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.NONE,
-                    new Insets(0, 2, 5, 185), 2, -4));
-    proxyPanel.add(proxyPortTB,
-            new GridBagConstraints(3, 1, 1, 1, 1.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-                    new Insets(0, 2, 2, 2), 54, 1));
-    proxyPanel.add(proxyServerTB,
-            new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-                    new Insets(0, 2, 2, 0), 263, 1));
+    GridBagConstraints gbc = new GridBagConstraints();
+    gbc.fill = GridBagConstraints.HORIZONTAL;
+    gbc.weightx = 1.0;
+
+    GridBagConstraints c = new GridBagConstraints();
+    // Proxy type radio buttons (3)
+    JPanel ptPanel = new JPanel();
+    ptPanel.setLayout(new GridBagLayout());
+    c.weightx = 1.0;
+    c.gridy = 0;
+    c.gridx = 0;
+    c.gridwidth = 1;
+    c.fill = GridBagConstraints.HORIZONTAL;
+    ptPanel.add(noProxy, c);
+    c.gridy++;
+    ptPanel.add(systemProxy, c);
+    c.gridy++;
+    ptPanel.add(customProxy, c);
+
+    gbc.gridy = 0;
+    proxyPanel.add(ptPanel, gbc);
+
+    // host and port text boxes
+    JPanel hpPanel = new JPanel();
+    hpPanel.setLayout(new GridBagLayout());
+    // HTTP host port row
+    c.gridy = 0;
+    c.gridx = 0;
+
+    c.weightx = 0.1;
+    c.anchor = GridBagConstraints.LINE_START;
+    hpPanel.add(httpLabel, c);
+
+    c.gridx++;
+    c.weightx = 0.1;
+    c.anchor = GridBagConstraints.LINE_END;
+    hpPanel.add(serverLabel, c);
+
+    c.gridx++;
+    c.weightx = 1.0;
+    c.anchor = GridBagConstraints.LINE_START;
+    hpPanel.add(proxyServerHttpTB, c);
+
+    c.gridx++;
+    c.weightx = 0.1;
+    c.anchor = GridBagConstraints.LINE_END;
+    hpPanel.add(portLabel, c);
+
+    c.gridx++;
+    c.weightx = 0.2;
+    c.anchor = GridBagConstraints.LINE_START;
+    hpPanel.add(proxyPortHttpTB, c);
+
+    // HTTPS host port row
+    c.gridy++;
+    c.gridx = 0;
+    c.gridwidth = 1;
+
+    c.anchor = GridBagConstraints.LINE_START;
+    hpPanel.add(httpsLabel, c);
+
+    c.gridx++;
+    c.anchor = GridBagConstraints.LINE_END;
+    hpPanel.add(serverLabel2, c);
+
+    c.gridx++;
+    c.anchor = GridBagConstraints.LINE_START;
+    hpPanel.add(proxyServerHttpsTB, c);
+
+    c.gridx++;
+    c.anchor = GridBagConstraints.LINE_END;
+    hpPanel.add(portLabel2, c);
+
+    c.gridx++;
+    c.anchor = GridBagConstraints.LINE_START;
+    hpPanel.add(proxyPortHttpsTB, c);
+
+    gbc.gridy++;
+    proxyPanel.add(hpPanel, gbc);
+
+    // Require authentication checkbox
+    gbc.gridy++;
+    proxyPanel.add(proxyAuth, gbc);
+
+    // username and password
+    JPanel upPanel = new JPanel();
+    upPanel.setLayout(new GridBagLayout());
+    // username row
+    c.gridy = 0;
+    c.gridx = 0;
+    c.gridwidth = 1;
+    c.weightx = 0.4;
+    c.anchor = GridBagConstraints.LINE_END;
+    upPanel.add(proxyAuthUsernameLabel, c);
+
+    c.gridx++;
+    c.weightx = 1.0;
+    c.anchor = GridBagConstraints.LINE_START;
+    upPanel.add(proxyAuthUsernameTB, c);
+
+    // password row
+    c.gridy++;
+    c.gridx = 0;
+    c.weightx = 0.4;
+    c.anchor = GridBagConstraints.LINE_END;
+    upPanel.add(proxyAuthPasswordLabel, c);
+
+    c.gridx++;
+    c.weightx = 1.0;
+    c.anchor = GridBagConstraints.LINE_START;
+    upPanel.add(proxyAuthPasswordPB, c);
+
+    c.gridx++;
+    c.weightx = 0.4;
+    c.anchor = GridBagConstraints.LINE_START;
+    upPanel.add(passwordNotStoredLabel, c);
+
+    gbc.gridy++;
+    proxyPanel.add(upPanel, gbc);
+
+    applyProxyButton.setText(MessageManager.getString("action.apply"));
+    applyProxyButton.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        saveProxySettings();
+        applyProxyButton.setEnabled(false);
+      }
+    });
+    gbc.gridy++;
+    gbc.fill = GridBagConstraints.NONE;
+    gbc.anchor = GridBagConstraints.LINE_END;
+    proxyPanel.add(applyProxyButton, gbc);
 
     return proxyPanel;
   }
 
+  public void proxyAuthPasswordCheckHighlight(boolean enabled)
+  {
+    proxyAuthPasswordCheckHighlight(enabled, false);
+  }
+
+  public void proxyAuthPasswordCheckHighlight(boolean enabled,
+          boolean grabFocus)
+  {
+    if (enabled && proxyType.isSelected(customProxy.getModel())
+            && proxyAuth.isSelected()
+            && !proxyAuthUsernameTB.getText().isEmpty()
+            && proxyAuthPasswordPB.getDocument().getLength() == 0)
+    {
+      if (grabFocus)
+        proxyAuthPasswordPB.grabFocus();
+      proxyAuthPasswordPB.setBackground(Color.PINK);
+    }
+    else
+    {
+      proxyAuthPasswordPB.setBackground(Color.WHITE);
+    }
+  }
+
+  public void applyProxyButtonEnabled(boolean enabled)
+  {
+    applyProxyButton.setEnabled(enabled);
+  }
+
+  public void saveProxySettings()
+  {
+    // overridden in Preferences
+  }
+
+  private String displayUserHostPort(String user, String host, String port)
+  {
+    boolean hostBlank = (host == null || host.isEmpty());
+    boolean portBlank = (port == null || port.isEmpty());
+    if (hostBlank && portBlank)
+    {
+      return MessageManager.getString("label.none");
+    }
+
+    StringBuilder sb = new StringBuilder();
+    if (user != null)
+    {
+      sb.append(user.isEmpty() || user.indexOf(" ") > -1 ? '"' + user + '"'
+              : user);
+      sb.append("@");
+    }
+    sb.append(hostBlank ? "" : host);
+    if (!portBlank)
+    {
+      sb.append(":");
+      sb.append(port);
+    }
+    return sb.toString();
+  }
+
   /**
    * Initialises the checkboxes in the Connections tab
    */
@@ -1309,11 +1693,13 @@ public class GPreferences extends JPanel
 
     ypos += lineSpacing;
     structureViewerPathLabel = new JLabel();
-    structureViewerPathLabel.setFont(LABEL_FONT);// new Font("SansSerif", 0, 11));
+    structureViewerPathLabel.setFont(LABEL_FONT);// new Font("SansSerif", 0,
+                                                 // 11));
     structureViewerPathLabel.setHorizontalAlignment(SwingConstants.LEFT);
     structureViewerPathLabel.setText(MessageManager
             .formatMessage("label.viewer_path", "Chimera(X)"));
-    structureViewerPathLabel.setBounds(new Rectangle(10, ypos, 170, height));
+    structureViewerPathLabel
+            .setBounds(new Rectangle(10, ypos, 170, height));
     structureViewerPathLabel.setEnabled(false);
     structureTab.add(structureViewerPathLabel);
 
@@ -1371,14 +1757,14 @@ public class GPreferences extends JPanel
     /*
      * hide Chimera options in JalviewJS
      */
-    if (Platform.isJS()) 
+    if (Platform.isJS())
     {
       structureViewerPathLabel.setVisible(false);
       structureViewerPath.setVisible(false);
       viewerLabel.setVisible(false);
       structViewer.setVisible(false);
     }
-    
+
     return structureTab;
   }
 
@@ -1755,13 +2141,13 @@ public class GPreferences extends JPanel
     visualTab.add(fontNameCB);
     visualTab.add(fontSizeCB);
     visualTab.add(fontStyleCB);
-    
+
     if (Platform.isJS())
     {
       startupCheckbox.setVisible(false);
       startupFileTextfield.setVisible(false);
     }
-    
+
     return visualTab;
   }
 
@@ -1773,8 +2159,8 @@ public class GPreferences extends JPanel
   {
     BackupFilesPresetEntry savedPreset = BackupFilesPresetEntry
             .getSavedBackupEntry();
-    enableBackupFiles
-            .setSelected(Cache.getDefault(BackupFiles.ENABLED, !Platform.isJS()));
+    enableBackupFiles.setSelected(
+            Cache.getDefault(BackupFiles.ENABLED, !Platform.isJS()));
 
     BackupFilesPresetEntry backupfilesCustomEntry = BackupFilesPresetEntry
             .createBackupFilesPresetEntry(Cache
@@ -1851,7 +2237,6 @@ public class GPreferences extends JPanel
       }
     });
 
-
     // enable checkbox 1 col
     gbc.gridwidth = 1;
     gbc.gridheight = 1;
@@ -1924,8 +2309,8 @@ public class GPreferences extends JPanel
     presetsComboLabel = new JLabel(title + ":");
     presetsPanel.add(presetsComboLabel, gbc);
 
-    List<Object> entries = Arrays
-            .asList((Object[]) BackupFilesPresetEntry.backupfilesPresetEntries);
+    List<Object> entries = Arrays.asList(
+            (Object[]) BackupFilesPresetEntry.backupfilesPresetEntries);
     List<String> tooltips = Arrays.asList(
             BackupFilesPresetEntry.backupfilesPresetEntryDescriptions);
     backupfilesPresetsCombo = JvSwingUtils.buildComboWithTooltips(entries,
@@ -1952,7 +2337,8 @@ public class GPreferences extends JPanel
         {
           if (customiseCheckbox.isSelected())
           {
-            // got here by clicking on customiseCheckbox so don't change the values
+            // got here by clicking on customiseCheckbox so don't change the
+            // values
             backupfilesCustomOptionsSetEnabled();
           }
           else
@@ -2031,13 +2417,11 @@ public class GPreferences extends JPanel
 
   private JPanel initBackupsTabFilenameExamplesPanel()
   {
-    String title = MessageManager
-            .getString("label.scheme_examples");
+    String title = MessageManager.getString("label.scheme_examples");
     TitledBorder tb = new TitledBorder(title);
     exampleFilesPanel.setBorder(tb);
     exampleFilesPanel.setLayout(new GridBagLayout());
 
-
     backupfilesExampleLabel.setEditable(false);
     backupfilesExampleLabel
             .setBackground(exampleFilesPanel.getBackground());
@@ -2098,8 +2482,7 @@ public class GPreferences extends JPanel
   }
 
   protected void setComboIntStringKey(
-          JComboBox<Object> backupfilesPresetsCombo2,
-          int key)
+          JComboBox<Object> backupfilesPresetsCombo2, int key)
   {
     for (int i = 0; i < backupfilesPresetsCombo2.getItemCount(); i++)
     {
@@ -2369,9 +2752,8 @@ public class GPreferences extends JPanel
 
     JPanel jp = new JPanel();
     jp.setLayout(new FlowLayout());
-    oldBackupFilesLabel
-            .setText(MessageManager
-                    .getString("label.autodelete_old_backup_files"));
+    oldBackupFilesLabel.setText(
+            MessageManager.getString("label.autodelete_old_backup_files"));
     oldBackupFilesLabel.setFont(LABEL_FONT);
     oldBackupFilesLabel.setHorizontalAlignment(SwingConstants.LEFT);
     jp.add(oldBackupFilesLabel);
@@ -2531,7 +2913,8 @@ public class GPreferences extends JPanel
 
     }
 
-    // add some extra empty lines to pad out the example files box. ugh, please tell
+    // add some extra empty lines to pad out the example files box. ugh, please
+    // tell
     // me how to do this better
     int remainingLines = lowersurround + uppersurround + 1 - lineNumber;
     if (remainingLines > 0)
@@ -2610,8 +2993,7 @@ public class GPreferences extends JPanel
   private void backupfilesKeepAllSetEnabled(boolean tryEnabled)
   {
     boolean enabled = tryEnabled && enableBackupFiles.isSelected()
-            && customiseCheckbox.isSelected()
-            && suffixTemplate.getText()
+            && customiseCheckbox.isSelected() && suffixTemplate.getText()
                     .indexOf(BackupFiles.NUM_PLACEHOLDER) > -1;
     keepfilesPanel.setEnabled(enabled);
     backupfilesKeepAll.setEnabled(enabled);
@@ -2823,13 +3205,45 @@ public class GPreferences extends JPanel
 
   }
 
-  public void useProxy_actionPerformed()
+  public void setProxyAuthEnabled()
+  {
+    boolean enabled = proxyAuth.isSelected() && proxyAuth.isEnabled();
+    proxyAuthUsernameLabel.setEnabled(enabled);
+    proxyAuthPasswordLabel.setEnabled(enabled);
+    passwordNotStoredLabel.setEnabled(enabled);
+    proxyAuthUsernameTB.setEnabled(enabled);
+    proxyAuthPasswordPB.setEnabled(enabled);
+  }
+
+  public void setCustomProxyEnabled()
   {
-    boolean enabled = useProxy.isSelected();
+    boolean enabled = customProxy.isSelected();
     portLabel.setEnabled(enabled);
     serverLabel.setEnabled(enabled);
-    proxyServerTB.setEnabled(enabled);
-    proxyPortTB.setEnabled(enabled);
+    portLabel2.setEnabled(enabled);
+    serverLabel2.setEnabled(enabled);
+    httpLabel.setEnabled(enabled);
+    httpsLabel.setEnabled(enabled);
+    proxyServerHttpTB.setEnabled(enabled);
+    proxyPortHttpTB.setEnabled(enabled);
+    proxyServerHttpsTB.setEnabled(enabled);
+    proxyPortHttpsTB.setEnabled(enabled);
+    proxyAuth.setEnabled(enabled);
+    setProxyAuthEnabled();
+  }
+
+  public void proxyType_actionPerformed()
+  {
+    setCustomProxyEnabled();
+    proxyAuthPasswordCheckHighlight(true);
+    applyProxyButtonEnabled(true);
+  }
+
+  public void proxyAuth_actionPerformed()
+  {
+    setProxyAuthEnabled();
+    proxyAuthPasswordCheckHighlight(true);
+    applyProxyButtonEnabled(true);
   }
 
   /**
@@ -2904,4 +3318,3 @@ public class GPreferences extends JPanel
 
   }
 }
-
index ccd9ab0..64b3a9c 100644 (file)
@@ -133,6 +133,7 @@ import jalview.schemes.UserColourScheme;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.Format;
+import jalview.util.HttpUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.StringUtils;
@@ -757,8 +758,8 @@ public class Jalview2XML
       // create backupfiles object and get new temp filename destination
       boolean doBackup = BackupFiles.getEnabled();
       BackupFiles backupfiles = doBackup ? new BackupFiles(jarFile) : null;
-      FileOutputStream fos = new FileOutputStream(doBackup ? 
-              backupfiles.getTempFilePath() : jarFile);
+      FileOutputStream fos = new FileOutputStream(
+              doBackup ? backupfiles.getTempFilePath() : jarFile);
 
       JarOutputStream jout = new JarOutputStream(fos);
       List<AlignFrame> frames = new ArrayList<>();
@@ -957,7 +958,7 @@ public class Jalview2XML
         else
         {
           vamsasSeq = createVamsasSequence(id, jds);
-//          vamsasSet.addSequence(vamsasSeq);
+          // vamsasSet.addSequence(vamsasSeq);
           vamsasSet.getSequence().add(vamsasSeq);
           vamsasSetIds.put(id, vamsasSeq);
           seqRefIds.put(id, jds);
@@ -1088,8 +1089,8 @@ public class Jalview2XML
             if (frames[f] instanceof StructureViewerBase)
             {
               StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
-              matchedFile = saveStructureViewer(ap, jds, pdb, entry, viewIds,
-                      matchedFile, viewFrame);
+              matchedFile = saveStructureViewer(ap, jds, pdb, entry,
+                      viewIds, matchedFile, viewFrame);
               /*
                * Only store each structure viewer's state once in the project
                * jar. First time through only (storeDS==false)
@@ -1107,9 +1108,8 @@ public class Jalview2XML
                 }
                 else
                 {
-                  Cache.log.error("Failed to save viewer state for "
-                          +
-                          viewerType);
+                  Cache.log.error(
+                          "Failed to save viewer state for " + viewerType);
                 }
               }
             }
@@ -1353,9 +1353,8 @@ public class Jalview2XML
 
             if (colourScheme instanceof jalview.schemes.UserColourScheme)
             {
-              jGroup.setColour(
-                      setUserColourScheme(colourScheme, userColours,
-                              object));
+              jGroup.setColour(setUserColourScheme(colourScheme,
+                      userColours, object));
             }
             else
             {
@@ -1401,7 +1400,7 @@ public class Jalview2XML
         }
       }
 
-      //jms.setJGroup(groups);
+      // jms.setJGroup(groups);
       Object group;
       for (JGroup grp : groups)
       {
@@ -1538,11 +1537,13 @@ public class Jalview2XML
              * save any filter for the feature type
              */
             FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
-            if (filter != null)  {
-              Iterator<FeatureMatcherI> filters = filter.getMatchers().iterator();
+            if (filter != null)
+            {
+              Iterator<FeatureMatcherI> filters = filter.getMatchers()
+                      .iterator();
               FeatureMatcherI firstFilter = filters.next();
-              setting.setMatcherSet(Jalview2XML.marshalFilter(
-                      firstFilter, filters, filter.isAnded()));
+              setting.setMatcherSet(Jalview2XML.marshalFilter(firstFilter,
+                      filters, filter.isAnded()));
             }
 
             /*
@@ -1591,8 +1592,7 @@ public class Jalview2XML
 
             setting.setDisplay(
                     av.getFeaturesDisplayed().isVisible(featureType));
-            float rorder = fr
-                    .getOrder(featureType);
+            float rorder = fr.getOrder(featureType);
             if (rorder > -1)
             {
               setting.setOrder(rorder);
@@ -1616,7 +1616,7 @@ public class Jalview2XML
           Group g = new Group();
           g.setName(grp);
           g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false))
-                          .booleanValue());
+                  .booleanValue());
           // fs.addGroup(g);
           fs.getGroup().add(g);
           groupsAdded.addElement(grp);
@@ -2373,7 +2373,8 @@ public class Jalview2XML
   {
     if (calcIdParam.getVersion().equals("1.0"))
     {
-      final String[] calcIds = calcIdParam.getServiceURL().toArray(new String[0]);
+      final String[] calcIds = calcIdParam.getServiceURL()
+              .toArray(new String[0]);
       Jws2Instance service = Jws2Discoverer.getDiscoverer()
               .getPreferredServiceFor(calcIds);
       if (service != null)
@@ -2534,8 +2535,8 @@ public class Jalview2XML
         }
         if (ref.hasMap())
         {
-          Mapping mp = createVamsasMapping(ref.getMap(), parentseq,
-                  jds, recurse);
+          Mapping mp = createVamsasMapping(ref.getMap(), parentseq, jds,
+                  recurse);
           dbref.setMapping(mp);
         }
         vamsasSeq.getDBRef().add(dbref);
@@ -2659,12 +2660,12 @@ public class Jalview2XML
     return id;
   }
 
-  jalview.schemes.UserColourScheme getUserColourScheme(
-          JalviewModel jm, String id)
+  jalview.schemes.UserColourScheme getUserColourScheme(JalviewModel jm,
+          String id)
   {
     List<UserColours> uc = jm.getUserColours();
     UserColours colours = null;
-/*
+    /*
     for (int i = 0; i < uc.length; i++)
     {
       if (uc[i].getId().equals(id))
@@ -2673,7 +2674,7 @@ public class Jalview2XML
         break;
       }
     }
-*/
+    */
     for (UserColours c : uc)
     {
       if (c.getId().equals(id))
@@ -2701,10 +2702,9 @@ public class Jalview2XML
       newColours = new java.awt.Color[23];
       for (int i = 0; i < 23; i++)
       {
-        newColours[i] = new java.awt.Color(Integer.parseInt(
-                colours.getUserColourScheme().getColour().get(i + 24)
-                        .getRGB(),
-                16));
+        newColours[i] = new java.awt.Color(
+                Integer.parseInt(colours.getUserColourScheme().getColour()
+                        .get(i + 24).getRGB(), 16));
       }
       ucs.setLowerCaseColours(newColours);
     }
@@ -2773,52 +2773,69 @@ public class Jalview2XML
     return af;
   }
 
-       @SuppressWarnings("unused")
-       private jarInputStreamProvider createjarInputStreamProvider(final Object ofile) throws MalformedURLException {
+  @SuppressWarnings("unused")
+  private jarInputStreamProvider createjarInputStreamProvider(
+          final Object ofile) throws MalformedURLException
+  {
 
-               // BH 2018 allow for bytes already attached to File object
-               try {
-                       String file = (ofile instanceof File ? ((File) ofile).getCanonicalPath() : ofile.toString());
+    // BH 2018 allow for bytes already attached to File object
+    try
+    {
+      String file = (ofile instanceof File
+              ? ((File) ofile).getCanonicalPath()
+              : ofile.toString());
       byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile)
               : null;
-                       URL url = null;
-                       errorMessage = null;
-                       uniqueSetSuffix = null;
-                       seqRefIds = null;
-                       viewportsAdded.clear();
-                       frefedSequence = null;
-
-                       if (file.startsWith("http://")) {
-                               url = new URL(file);
-                       }
-                       final URL _url = url;
-                       return new jarInputStreamProvider() {
-
-                               @Override
-                               public JarInputStream getJarInputStream() throws IOException {
-                                       if (bytes != null) {
-//                                             System.out.println("Jalview2XML: opening byte jarInputStream for bytes.length=" + bytes.length);
-                                               return new JarInputStream(new ByteArrayInputStream(bytes));
-                                       }
-                                       if (_url != null) {
-//                                             System.out.println("Jalview2XML: opening url jarInputStream for " + _url);
-                                               return new JarInputStream(_url.openStream());
-                                       } else {
-//                                             System.out.println("Jalview2XML: opening file jarInputStream for " + file);
-                                               return new JarInputStream(new FileInputStream(file));
-                                       }
-                               }
-
-                               @Override
-                               public String getFilename() {
-                                       return file;
-                               }
-                       };
-               } catch (IOException e) {
-                       e.printStackTrace();
-                       return null;
-               }
-       }
+      URL url = null;
+      errorMessage = null;
+      uniqueSetSuffix = null;
+      seqRefIds = null;
+      viewportsAdded.clear();
+      frefedSequence = null;
+
+      if (HttpUtils.startsWithHttpOrHttps(file))
+      {
+        url = new URL(file);
+      }
+      final URL _url = url;
+      return new jarInputStreamProvider()
+      {
+
+        @Override
+        public JarInputStream getJarInputStream() throws IOException
+        {
+          if (bytes != null)
+          {
+            // System.out.println("Jalview2XML: opening byte jarInputStream for
+            // bytes.length=" + bytes.length);
+            return new JarInputStream(new ByteArrayInputStream(bytes));
+          }
+          if (_url != null)
+          {
+            // System.out.println("Jalview2XML: opening url jarInputStream for "
+            // + _url);
+            return new JarInputStream(_url.openStream());
+          }
+          else
+          {
+            // System.out.println("Jalview2XML: opening file jarInputStream for
+            // " + file);
+            return new JarInputStream(new FileInputStream(file));
+          }
+        }
+
+        @Override
+        public String getFilename()
+        {
+          return file;
+        }
+      };
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+      return null;
+    }
+  }
 
   /**
    * Recover jalview session from a jalview project archive. Caller may
@@ -2865,8 +2882,8 @@ public class Jalview2XML
           XMLStreamReader streamReader = XMLInputFactory.newInstance()
                   .createXMLStreamReader(jin);
           javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
-          JAXBElement<JalviewModel> jbe = um
-                  .unmarshal(streamReader, JalviewModel.class);
+          JAXBElement<JalviewModel> jbe = um.unmarshal(streamReader,
+                  JalviewModel.class);
           JalviewModel object = jbe.getValue();
 
           if (true) // !skipViewport(object))
@@ -3254,7 +3271,8 @@ public class Jalview2XML
   AlignFrame loadFromObject(JalviewModel jalviewModel, String file,
           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
   {
-    SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet().get(0);
+    SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet()
+            .get(0);
     List<Sequence> vamsasSeqs = vamsasSet.getSequence();
 
     // JalviewModelSequence jms = object.getJalviewModelSequence();
@@ -3313,11 +3331,10 @@ public class Jalview2XML
           if (tmpSeq.getStart() != jseq.getStart()
                   || tmpSeq.getEnd() != jseq.getEnd())
           {
-            System.err.println(
-                    String.format("Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d",
-                            tmpSeq.getName(), tmpSeq.getStart(),
-                            tmpSeq.getEnd(), jseq.getStart(),
-                            jseq.getEnd()));
+            System.err.println(String.format(
+                    "Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d",
+                    tmpSeq.getName(), tmpSeq.getStart(), tmpSeq.getEnd(),
+                    jseq.getStart(), jseq.getEnd()));
           }
         }
         else
@@ -3600,8 +3617,8 @@ public class Jalview2XML
               else
               {
                 // defer to later
-                frefedSequence.add(
-                        newAlcodMapRef(map.getDnasq(), cf, mapping));
+                frefedSequence
+                        .add(newAlcodMapRef(map.getDnasq(), cf, mapping));
               }
             }
           }
@@ -3809,8 +3826,7 @@ public class Jalview2XML
         jaa.setCalcId(annotation.getCalcId());
         if (annotation.getProperty().size() > 0)
         {
-          for (Annotation.Property prop : annotation
-                  .getProperty())
+          for (Annotation.Property prop : annotation.getProperty())
           {
             jaa.setProperty(prop.getName(), prop.getValue());
           }
@@ -3891,9 +3907,9 @@ public class Jalview2XML
         sg.setShowNonconserved(safeBoolean(jGroup.isShowUnconserved()));
         sg.thresholdTextColour = safeInt(jGroup.getTextColThreshold());
         // attributes with a default in the schema are never null
-          sg.setShowConsensusHistogram(jGroup.isShowConsensusHistogram());
-          sg.setshowSequenceLogo(jGroup.isShowSequenceLogo());
-          sg.setNormaliseSequenceLogo(jGroup.isNormaliseSequenceLogo());
+        sg.setShowConsensusHistogram(jGroup.isShowConsensusHistogram());
+        sg.setshowSequenceLogo(jGroup.isShowSequenceLogo());
+        sg.setNormaliseSequenceLogo(jGroup.isNormaliseSequenceLogo());
         sg.setIgnoreGapsConsensus(jGroup.isIgnoreGapsinConsensus());
         if (jGroup.getConsThreshold() != null
                 && jGroup.getConsThreshold().intValue() != 0)
@@ -3937,8 +3953,9 @@ public class Jalview2XML
         if (addAnnotSchemeGroup)
         {
           // reconstruct the annotation colourscheme
-          sg.setColourScheme(constructAnnotationColour(
-                  jGroup.getAnnotationColours(), null, al, jalviewModel, false));
+          sg.setColourScheme(
+                  constructAnnotationColour(jGroup.getAnnotationColours(),
+                          null, al, jalviewModel, false));
         }
       }
     }
@@ -4144,8 +4161,8 @@ public class Jalview2XML
    * @param av
    * @param ap
    */
-  protected void loadTrees(JalviewModel jm, Viewport view,
-          AlignFrame af, AlignViewport av, AlignmentPanel ap)
+  protected void loadTrees(JalviewModel jm, Viewport view, AlignFrame af,
+          AlignViewport av, AlignmentPanel ap)
   {
     // TODO result of automated refactoring - are all these parameters needed?
     try
@@ -4253,8 +4270,8 @@ public class Jalview2XML
           for (int s = 0; s < structureStateCount; s++)
           {
             // check to see if we haven't already created this structure view
-            final StructureState structureState = pdbid
-                    .getStructureState().get(s);
+            final StructureState structureState = pdbid.getStructureState()
+                    .get(s);
             String sviewid = (structureState.getViewId() == null) ? null
                     : structureState.getViewId() + uniqueSetSuffix;
             jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
@@ -4322,8 +4339,8 @@ public class Jalview2XML
             colourByViewer &= structureState.isColourByJmol();
             jmoldat.setColourByViewer(colourByViewer);
 
-            if (jmoldat.getStateData().length() < structureState
-                    .getValue()/*Content()*/.length())
+            if (jmoldat.getStateData().length() < structureState.getValue()
+                    /*Content()*/.length())
             {
               jmoldat.setStateData(structureState.getValue());// Content());
             }
@@ -4401,8 +4418,7 @@ public class Jalview2XML
     } catch (IllegalArgumentException | NullPointerException e)
     {
       // TODO JAL-3619 show error dialog / offer an alternative viewer
-      Cache.log.error(
-              "Invalid structure viewer type: " + type);
+      Cache.log.error("Invalid structure viewer type: " + type);
     }
   }
 
@@ -4602,23 +4618,23 @@ public class Jalview2XML
   }
 
   AlignFrame loadViewport(String file, List<JSeq> JSEQ,
-          List<SequenceI> hiddenSeqs, AlignmentI al,
-          JalviewModel jm, Viewport view, String uniqueSeqSetId,
-          String viewId, List<JvAnnotRow> autoAlan)
+          List<SequenceI> hiddenSeqs, AlignmentI al, JalviewModel jm,
+          Viewport view, String uniqueSeqSetId, String viewId,
+          List<JvAnnotRow> autoAlan)
   {
     AlignFrame af = null;
     af = new AlignFrame(al, safeInt(view.getWidth()),
-            safeInt(view.getHeight()), uniqueSeqSetId, viewId) 
-//    {
-//     
-//     @Override
-//     protected void processKeyEvent(java.awt.event.KeyEvent e) {
-//             System.out.println("Jalview2XML   AF " + e);
-//             super.processKeyEvent(e);
-//             
-//     }
-//     
-//    }
+            safeInt(view.getHeight()), uniqueSeqSetId, viewId)
+    // {
+    //
+    // @Override
+    // protected void processKeyEvent(java.awt.event.KeyEvent e) {
+    // System.out.println("Jalview2XML AF " + e);
+    // super.processKeyEvent(e);
+    //
+    // }
+    //
+    // }
     ;
 
     af.setFileName(file, FileFormat.Jalview);
@@ -4696,9 +4712,8 @@ public class Jalview2XML
 
     viewport.setColourText(safeBoolean(view.isShowColourText()));
 
-    viewport
-            .setConservationSelected(
-                    safeBoolean(view.isConservationSelected()));
+    viewport.setConservationSelected(
+            safeBoolean(view.isConservationSelected()));
     viewport.setIncrement(safeInt(view.getConsThreshold()));
     viewport.setShowJVSuffix(safeBoolean(view.isShowFullId()));
     viewport.setRightAlignIds(safeBoolean(view.isRightAlignIds()));
@@ -4774,9 +4789,8 @@ public class Jalview2XML
     af.changeColour(cs);
     viewport.setColourAppliesToAllGroups(true);
 
-    viewport
-            .setShowSequenceFeatures(
-                    safeBoolean(view.isShowSequenceFeatures()));
+    viewport.setShowSequenceFeatures(
+            safeBoolean(view.isShowSequenceFeatures()));
 
     viewport.setCentreColumnLabels(view.isCentreColumnLabels());
     viewport.setIgnoreGapsConsensus(view.isIgnoreGapsinConsensus(), null);
@@ -4800,13 +4814,13 @@ public class Jalview2XML
               .getFeatureRenderer();
       FeaturesDisplayed fdi;
       viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
-      String[] renderOrder = new String[jm.getFeatureSettings()
-              .getSetting().size()];
+      String[] renderOrder = new String[jm.getFeatureSettings().getSetting()
+              .size()];
       Map<String, FeatureColourI> featureColours = new Hashtable<>();
       Map<String, Float> featureOrder = new Hashtable<>();
 
-      for (int fs = 0; fs < jm.getFeatureSettings()
-              .getSetting().size(); fs++)
+      for (int fs = 0; fs < jm.getFeatureSettings().getSetting()
+              .size(); fs++)
       {
         Setting setting = jm.getFeatureSettings().getSetting().get(fs);
         String featureType = setting.getType();
@@ -4818,8 +4832,8 @@ public class Jalview2XML
                 .getMatcherSet();
         if (filters != null)
         {
-          FeatureMatcherSetI filter = Jalview2XML
-                  .parseFilter(featureType, filters);
+          FeatureMatcherSetI filter = Jalview2XML.parseFilter(featureType,
+                  filters);
           if (!filter.isEmpty())
           {
             fr.setFeatureFilter(featureType, filter);
@@ -4851,8 +4865,7 @@ public class Jalview2XML
           float max = setting.getMax() == null ? 1f
                   : setting.getMax().floatValue();
           FeatureColourI gc = new FeatureColour(maxColour, minColour,
-                  maxColour,
-                  noValueColour, min, max);
+                  maxColour, noValueColour, min, max);
           if (setting.getAttributeName().size() > 0)
           {
             gc.setAttributeName(setting.getAttributeName().toArray(
@@ -4886,8 +4899,7 @@ public class Jalview2XML
         }
         else
         {
-          featureColours.put(featureType,
-                  new FeatureColour(maxColour));
+          featureColours.put(featureType, new FeatureColour(maxColour));
         }
         renderOrder[fs] = featureType;
         if (setting.getOrder() != null)
@@ -5360,6 +5372,7 @@ public class Jalview2XML
       }
     }
   }
+
   /**
    * 
    * @param vamsasSeq
@@ -6108,8 +6121,8 @@ public class Jalview2XML
    * @param af
    * @param jprovider
    */
-  protected void createStructureViewer(
-          ViewerType viewerType, final Entry<String, StructureViewerModel> viewerData,
+  protected void createStructureViewer(ViewerType viewerType,
+          final Entry<String, StructureViewerModel> viewerData,
           AlignFrame af, jarInputStreamProvider jprovider)
   {
     final StructureViewerModel viewerModel = viewerData.getValue();
@@ -6123,8 +6136,7 @@ public class Jalview2XML
     {
       String viewerJarEntryName = getViewerJarEntryName(
               viewerModel.getViewId());
-      sessionFilePath = copyJarEntry(jprovider,
-              viewerJarEntryName,
+      sessionFilePath = copyJarEntry(jprovider, viewerJarEntryName,
               "viewerSession", ".tmp");
     }
     final String sessionPath = sessionFilePath;
@@ -6144,8 +6156,7 @@ public class Jalview2XML
             addNewStructureViewer(sview);
           } catch (OutOfMemoryError ex)
           {
-            new OOMWarning("Restoring structure view for "
-                    + viewerType,
+            new OOMWarning("Restoring structure view for " + viewerType,
                     (OutOfMemoryError) ex.getCause());
             if (sview != null && sview.isVisible())
             {
@@ -6205,8 +6216,7 @@ public class Jalview2XML
           String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
           filedat = oldFiles.get(new File(reformatedOldFilename));
         }
-        rewritten
-                .append(Platform.escapeBackslashes(filedat.getFilePath()));
+        rewritten.append(Platform.escapeBackslashes(filedat.getFilePath()));
         rewritten.append("\"");
         cp = ecp + 1; // advance beyond last \" and set cursor so we can
                       // look for next file statement.
@@ -6278,8 +6288,8 @@ public class Jalview2XML
    * @param fcol
    * @return
    */
-  public static Colour marshalColour(
-          String featureType, FeatureColourI fcol)
+  public static Colour marshalColour(String featureType,
+          FeatureColourI fcol)
   {
     Colour col = new Colour();
     if (fcol.isSimpleColour())
@@ -6340,7 +6350,7 @@ public class Jalview2XML
           boolean and)
   {
     jalview.xml.binding.jalview.FeatureMatcherSet result = new jalview.xml.binding.jalview.FeatureMatcherSet();
-  
+
     if (filters.hasNext())
     {
       /*
@@ -6390,7 +6400,7 @@ public class Jalview2XML
       }
       result.setMatchCondition(matcherModel);
     }
-  
+
     return result;
   }
 
@@ -6401,8 +6411,7 @@ public class Jalview2XML
    * @param matcherSetModel
    * @return
    */
-  public static FeatureMatcherSetI parseFilter(
-          String featureType,
+  public static FeatureMatcherSetI parseFilter(String featureType,
           jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel)
   {
     FeatureMatcherSetI result = new FeatureMatcherSet();
@@ -6417,7 +6426,7 @@ public class Jalview2XML
                       featureType, e.getMessage()));
       // return as much as was parsed up to the error
     }
-  
+
     return result;
   }
 
@@ -6432,8 +6441,7 @@ public class Jalview2XML
    * @throws IllegalStateException
    *           if AND and OR conditions are mixed
    */
-  protected static void parseFilterConditions(
-          FeatureMatcherSetI matcherSet,
+  protected static void parseFilterConditions(FeatureMatcherSetI matcherSet,
           jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel,
           boolean and)
   {
@@ -6455,7 +6463,7 @@ public class Jalview2XML
       else if (filterBy == FilterBy.BY_SCORE)
       {
         matchCondition = FeatureMatcher.byScore(cond, pattern);
-  
+
       }
       else if (filterBy == FilterBy.BY_ATTRIBUTE)
       {
@@ -6465,7 +6473,7 @@ public class Jalview2XML
         matchCondition = FeatureMatcher.byAttribute(cond, pattern,
                 attNames);
       }
-  
+
       /*
        * note this throws IllegalStateException if AND-ing to a 
        * previously OR-ed compound condition, or vice versa
@@ -6508,13 +6516,13 @@ public class Jalview2XML
   public static FeatureColourI parseColour(Colour colourModel)
   {
     FeatureColourI colour = null;
-  
+
     if (colourModel.getMax() != null)
     {
       Color mincol = null;
       Color maxcol = null;
       Color noValueColour = null;
-  
+
       try
       {
         mincol = new Color(Integer.parseInt(colourModel.getMinRGB(), 16));
@@ -6523,7 +6531,7 @@ public class Jalview2XML
       {
         Cache.log.warn("Couldn't parse out graduated feature color.", e);
       }
-  
+
       NoValueColour noCol = colourModel.getNoValueColour();
       if (noCol == NoValueColour.MIN)
       {
@@ -6533,7 +6541,7 @@ public class Jalview2XML
       {
         noValueColour = maxcol;
       }
-  
+
       colour = new FeatureColour(maxcol, mincol, maxcol, noValueColour,
               safeFloat(colourModel.getMin()),
               safeFloat(colourModel.getMax()));
@@ -6572,7 +6580,7 @@ public class Jalview2XML
       Color color = new Color(Integer.parseInt(colourModel.getRGB(), 16));
       colour = new FeatureColour(color);
     }
-  
+
     return colour;
   }
 }
index a5a9460..74f77a2 100644 (file)
@@ -64,4 +64,9 @@ public class HttpUtils
     return false;
   }
 
+  public static boolean startsWithHttpOrHttps(String file)
+  {
+    return file.startsWith("http://") || file.startsWith("https://");
+  }
+
 }
index 4fb9ca9..ae58082 100644 (file)
@@ -58,6 +58,7 @@ import jalview.api.SiftsClientI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceI;
+import jalview.io.BackupFiles;
 import jalview.io.StructureFile;
 import jalview.schemes.ResidueProperties;
 import jalview.structure.StructureMapping;
@@ -124,6 +125,7 @@ public class SiftsClient implements SiftsClientI
   private enum CoordinateSys
   {
     UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe");
+
     private String name;
 
     private CoordinateSys(String name)
@@ -141,6 +143,7 @@ public class SiftsClient implements SiftsClientI
   {
     NAME_SEC_STRUCTURE("nameSecondaryStructure"),
     CODE_SEC_STRUCTURE("codeSecondaryStructure"), ANNOTATION("Annotation");
+
     private String code;
 
     private ResidueDetailType(String code)
@@ -227,7 +230,7 @@ public class SiftsClient implements SiftsClientI
               SiftsSettings.getCacheThresholdInDays()))
       {
         File oldSiftsFile = new File(siftsFileName + "_old");
-        siftsFile.renameTo(oldSiftsFile);
+        BackupFiles.moveFileToFile(siftsFile, oldSiftsFile);
         try
         {
           siftsFile = downloadSiftsFile(pdbId.toLowerCase());
@@ -236,7 +239,7 @@ public class SiftsClient implements SiftsClientI
         } catch (IOException e)
         {
           e.printStackTrace();
-          oldSiftsFile.renameTo(siftsFile);
+          BackupFiles.moveFileToFile(oldSiftsFile, siftsFile);
           return new File(siftsFileName);
         }
       }
@@ -301,7 +304,7 @@ public class SiftsClient implements SiftsClientI
     }
     String siftFile = pdbId + ".xml.gz";
     String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile;
-    
+
     /*
      * Download the file from URL to either
      * Java: directory of cached downloaded SIFTS files
@@ -329,8 +332,7 @@ public class SiftsClient implements SiftsClientI
     URL url = new URL(siftsFileFTPURL);
     URLConnection conn = url.openConnection();
     InputStream inputStream = conn.getInputStream();
-    FileOutputStream outputStream = new FileOutputStream(
-            downloadTo);
+    FileOutputStream outputStream = new FileOutputStream(downloadTo);
     byte[] buffer = new byte[BUFFER_SIZE];
     int bytesRead = -1;
     while ((bytesRead = inputStream.read(buffer)) != -1)
@@ -478,7 +480,7 @@ public class SiftsClient implements SiftsClientI
           SequenceI seq, java.io.PrintStream os) throws SiftsException
   {
     List<Integer> omitNonObserved = new ArrayList<>();
-    int nonObservedShiftIndex = 0,pdbeNonObserved=0;
+    int nonObservedShiftIndex = 0, pdbeNonObserved = 0;
     // System.out.println("Generating mappings for : " + entityId);
     Entity entity = null;
     entity = getEntityById(entityId);
@@ -509,7 +511,7 @@ public class SiftsClient implements SiftsClientI
     TreeMap<Integer, String> resNumMap = new TreeMap<Integer, String>();
     List<Segment> segments = entity.getSegment();
     SegmentHelperPojo shp = new SegmentHelperPojo(seq, mapping, resNumMap,
-            omitNonObserved, nonObservedShiftIndex,pdbeNonObserved);
+            omitNonObserved, nonObservedShiftIndex, pdbeNonObserved);
     processSegments(segments, shp);
     try
     {
@@ -531,18 +533,20 @@ public class SiftsClient implements SiftsClientI
     {
       throw new SiftsException("SIFTS mapping failed");
     }
-    // also construct a mapping object between the seq-coord sys and the PDB seq's coord sys
+    // also construct a mapping object between the seq-coord sys and the PDB
+    // seq's coord sys
 
     Integer[] keys = mapping.keySet().toArray(new Integer[0]);
     Arrays.sort(keys);
     seqStart = keys[0];
     seqEnd = keys[keys.length - 1];
-    List<int[]> from=new ArrayList<>(),to=new ArrayList<>();
-    int[]_cfrom=null,_cto=null;
+    List<int[]> from = new ArrayList<>(), to = new ArrayList<>();
+    int[] _cfrom = null, _cto = null;
     String matchedSeq = originalSeq;
-    if (seqStart != UNASSIGNED) // fixme! seqStart can map to -1 for a pdb sequence that starts <-1
+    if (seqStart != UNASSIGNED) // fixme! seqStart can map to -1 for a pdb
+                                // sequence that starts <-1
     {
-      for (int seqps:keys)
+      for (int seqps : keys)
       {
         int pdbpos = mapping.get(seqps)[PDBE_POS];
         if (pdbpos == UNASSIGNED)
@@ -550,19 +554,23 @@ public class SiftsClient implements SiftsClientI
           // not correct - pdbpos might be -1, but leave it for now
           continue;
         }
-        if (_cfrom==null || seqps!=_cfrom[1]+1)
+        if (_cfrom == null || seqps != _cfrom[1] + 1)
         {
-          _cfrom = new int[] { seqps,seqps};
+          _cfrom = new int[] { seqps, seqps };
           from.add(_cfrom);
           _cto = null; // discontinuity
-        } else {
-          _cfrom[1]= seqps;
         }
-        if (_cto==null || pdbpos!=1+_cto[1])
+        else
+        {
+          _cfrom[1] = seqps;
+        }
+        if (_cto == null || pdbpos != 1 + _cto[1])
         {
-          _cto = new int[] { pdbpos,pdbpos};
+          _cto = new int[] { pdbpos, pdbpos };
           to.add(_cto);
-        } else {
+        }
+        else
+        {
           _cto[1] = pdbpos;
         }
       }
@@ -584,8 +592,7 @@ public class SiftsClient implements SiftsClientI
       ;
 
       seqFromPdbMapping = new jalview.datamodel.Mapping(null, _cto, _cfrom,
-              1,
-              1);
+              1, 1);
       pdbStart = mapping.get(seqStart)[PDB_RES_POS];
       pdbEnd = mapping.get(seqEnd)[PDB_RES_POS];
       int orignalSeqStart = seq.getStart();
@@ -716,12 +723,12 @@ public class SiftsClient implements SiftsClientI
         }
         // if (currSeqIndex >= seq.getStart() && currSeqIndex <= seqlength) //
         // true
-                                                                         // numbering
-                                                                         // is
-                                                                         // not
-                                                                         // up
-                                                                         // to
-                                                                         // seq.getEnd()
+        // numbering
+        // is
+        // not
+        // up
+        // to
+        // seq.getEnd()
         {
 
           int resNum = (pdbRefDb == null)
@@ -1061,6 +1068,7 @@ public class SiftsClient implements SiftsClientI
     {
       return pdbeNonObserved;
     }
+
     public SequenceI getSeq()
     {
       return seq;
diff --git a/test/jalview/bin/HiDPISettingTest1.java b/test/jalview/bin/HiDPISettingTest1.java
new file mode 100644 (file)
index 0000000..fd27a34
--- /dev/null
@@ -0,0 +1,116 @@
+package jalview.bin;
+
+import static org.testng.Assert.assertEquals;
+
+import org.mockito.Mockito;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+
+/*
+ * Testing a HiDPI display is difficult without running in a HiDPI display.
+ * The gathering of screen size and resolution performed by jalview.bin.HiDPISetting
+ * is now farmed out into a separate class jalview.bin.ScreenInfo so it can be more
+ * easily Mocked in tests.
+ * Two sets of tests are performed.
+ * 1) testLinuxScalePropertyToActualTransform() sets the property that HiDPISetting
+ * uses (via jalview.bin.Launcher or getdown) to scale up Jalview, and then looks at
+ * the alignment panel graphics2d transform to see if it's been scaled by the same
+ * amount (in this case 4 -- unlikely to happen by accident!).
+ * 2) testHiDPISettingInit() which tests the calculation that HiDPISetting uses to
+ * decide from apparent screen information (mocked using Mockito in the tests) what
+ * scaling factor to set (it doesn't actually set it, just suggests it to
+ * jalview.bin.Launcher)
+ */
+public class HiDPISettingTest1
+{
+
+  AlignFrame af = null;
+
+  @BeforeMethod(alwaysRun = true)
+  public void setUp()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+    Cache.loadProperties("test/jalview/bin/hidpiTestProps.jvprops");
+    Jalview.main(
+            new String[]
+            { "-nosplash", "-nonews", "-noquestionnaire",
+                "-nowebservicediscovery" });
+
+    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+            DataSourceType.FILE);
+
+    /*
+     * wait for Consensus thread to complete
+     */
+    do
+    {
+      try
+      {
+        Thread.sleep(50);
+      } catch (InterruptedException x)
+      {
+      }
+    } while (af.getViewport().getCalcManager().isWorking());
+  }
+
+  @AfterClass(alwaysRun = true)
+  public void tearDown()
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+  }
+
+  @Test(groups = { "Functional" })
+  public void testHiDPISettingInit()
+  {
+    String scalePropertyName = "sun.java2d.uiScale";
+    // ensure scale property is cleared (otherwise it will be re-used)
+    System.clearProperty(HiDPISetting.scalePropertyName);
+
+    { // keep the mock under lock
+      // Ancient monitor -- no property change set
+      setMockScreen(1024, 768, 72);
+      assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+      // Old monitor -- no property change set
+      setMockScreen(1200, 800, 96);
+      assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+      // HD screen -- no property change set
+      setMockScreen(1920, 1080, 96);
+      assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+      // 4K screen -- scale by 2
+      setMockScreen(3180, 2160, 80);
+      assertEquals(HiDPISetting.getScalePropertyArg(),
+              "-D" + scalePropertyName + "=2");
+
+      // 4K screen with high dpi -- scale by 3
+      setMockScreen(3180, 2160, 450);
+      assertEquals(HiDPISetting.getScalePropertyArg(),
+              "-D" + scalePropertyName + "=3");
+
+      // stupidly big screen -- scale by 8
+      setMockScreen(19200, 10800, 72);
+      assertEquals(HiDPISetting.getScalePropertyArg(),
+              "-D" + scalePropertyName + "=8");
+    }
+  }
+
+  private void setMockScreen(int width, int height, int dpi)
+  {
+    HiDPISetting.clear();
+    ScreenInfo mockScreenInfo = Mockito.spy(HiDPISetting.getScreenInfo());
+    Mockito.doReturn(height).when(mockScreenInfo).getScreenHeight();
+    Mockito.doReturn(width).when(mockScreenInfo).getScreenWidth();
+    Mockito.doReturn(dpi).when(mockScreenInfo).getScreenResolution();
+    HiDPISetting.setScreenInfo(mockScreenInfo);
+  }
+}
diff --git a/test/jalview/bin/HiDPISettingTest2.java b/test/jalview/bin/HiDPISettingTest2.java
new file mode 100644 (file)
index 0000000..71d13d0
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.bin;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.Assert;
+import org.testng.SkipException;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ScanResult;
+import jalview.gui.Desktop;
+import jalview.util.Platform;
+
+public class HiDPISettingTest2
+{
+  private static final int TIMEOUT = 10;
+
+  private static class Worker extends Thread
+  {
+    private final Process process;
+
+    private BufferedReader outputReader;
+
+    private BufferedReader errorReader;
+
+    private boolean exited;
+
+    private Worker(Process process)
+    {
+      this.process = process;
+    }
+
+    @Override
+    public void run()
+    {
+      try
+      {
+        exited = process.waitFor(TIMEOUT, TimeUnit.SECONDS);
+      } catch (InterruptedException ignore)
+      {
+        return;
+      }
+      this.interrupt();
+      this.process.destroy();
+    }
+
+    public BufferedReader getOutputReader()
+    {
+      return outputReader;
+    }
+
+    public void setOutputReader(BufferedReader outputReader)
+    {
+      this.outputReader = outputReader;
+    }
+
+    public BufferedReader getErrorReader()
+    {
+      return errorReader;
+    }
+
+    public void setErrorReader(BufferedReader errorReader)
+    {
+      this.errorReader = errorReader;
+    }
+  }
+
+  private static ClassGraph scanner = null;
+
+  private static String classpath = null;
+
+  private static String java_exe = null;
+
+  public synchronized static String getClassPath()
+  {
+    if (scanner == null)
+    {
+      scanner = new ClassGraph();
+      ScanResult scan = scanner.scan();
+      classpath = scan.getClasspath();
+      java_exe = System.getProperty("java.home") + File.separator + "bin"
+              + File.separator + "java";
+
+    }
+    while (classpath == null)
+    {
+      try
+      {
+        Thread.sleep(10);
+      } catch (InterruptedException x)
+      {
+
+      }
+    }
+    return classpath;
+  }
+
+  private Worker getJalviewDesktopRunner(String jvmArgs, String appArgs)
+  {
+    String classpath = getClassPath();
+    String cmd = java_exe + " " + " -classpath " + classpath + " " + jvmArgs
+            + " jalview.bin.Jalview " + " "
+            + "-props test/jalview/bin/hidpiTestProps.jvprops " + appArgs;
+    Process proc = null;
+    Worker worker = null;
+    try
+    {
+      proc = Runtime.getRuntime().exec(cmd);
+    } catch (Throwable e)
+    {
+      e.printStackTrace();
+    }
+    if (proc != null)
+    {
+      BufferedReader outputReader = new BufferedReader(
+              new InputStreamReader(proc.getInputStream()));
+      BufferedReader errorReader = new BufferedReader(
+              new InputStreamReader(proc.getErrorStream()));
+      worker = new Worker(proc);
+      worker.start();
+      worker.setOutputReader(outputReader);
+      worker.setErrorReader(errorReader);
+    }
+    return worker;
+  }
+
+  @BeforeTest(alwaysRun = true)
+  public void initialize()
+  {
+    new HiDPISettingTest2();
+  }
+
+  @Test(groups = { "Functional" }, dataProvider = "hidpiScaleArguments")
+  public void testHiDPISettings(int scale)
+  {
+    if (Platform.isLinux())
+    {
+      throw new SkipException(
+              "Not linux platform, not testing actual scaling with "
+                      + HiDPISetting.scalePropertyName);
+    }
+
+    String jvmArgs = HiDPISetting.getScalePropertyArg(scale);
+
+    String appArgs = " -open examples/uniref50.fa -nosplash -nonews -noquestionnaire -nousagestats -nowebservicediscovery";
+
+    Worker worker = getJalviewDesktopRunner(jvmArgs, appArgs);
+    assertNotNull(worker, "worker is null");
+
+    String ln = null;
+    int count = 0;
+    boolean scaleFound = false;
+    try
+    {
+      while ((ln = worker.getErrorReader().readLine()) != null)
+      {
+        if (++count > 100)
+        {
+          break;
+        }
+        if (ln.contains(Desktop.debugScaleMessage))
+        {
+          String number = ln.substring(ln.indexOf(Desktop.debugScaleMessage)
+                  + Desktop.debugScaleMessage.length());
+          number = number.substring(0, number.indexOf(' '));
+          try
+          {
+            double d = Double.valueOf(number);
+
+            assertEquals(d, scale * 1.0);
+            scaleFound = true;
+          } catch (NumberFormatException e)
+          {
+            e.printStackTrace();
+            Assert.fail(
+                    "Debug scale message line '" + ln + "' gives number '"
+                            + number + "' which could not be parsed");
+          }
+          break;
+        }
+      }
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+    if (worker != null && worker.exited == false)
+    {
+      worker.interrupt();
+      worker.process.destroy();
+    }
+    if (!scaleFound)
+    {
+      Assert.fail("Did not find Debug scale message line '"
+              + Desktop.debugScaleMessage + "'");
+    }
+  }
+
+  @DataProvider(name = "hidpiScaleArguments")
+  public static Object[][] getHiDPIScaleArguments()
+  {
+    return new Object[][] { { 1 }, { 2 }, { 4 }, { 10 } };
+  }
+}
diff --git a/test/jalview/bin/hidpiTestProps.jvprops b/test/jalview/bin/hidpiTestProps.jvprops
new file mode 100644 (file)
index 0000000..c640dd1
--- /dev/null
@@ -0,0 +1,15 @@
+#---JalviewX Properties File---
+# HiDPI screen
+SCREENGEOMETRY_WIDTH=3840
+SCREENGEOMETRY_HEIGHT=2160
+SCREEN_WIDTH=3000
+SCREEN_HEIGHT=2000
+USAGESTATS=false
+STARTUP_FILE=examples/uniref50.fa
+SHOW_STARTUP_FILE=false
+FONT_STYLE=plain
+FONT_SIZE=10
+ANTI_ALIAS=false
+SHOW_JAVA_CONSOLE=false
+DAS_REGISTRY_URL=http\://www.ebi.ac.uk/das-srv/registry/das/
+logs.Jalview.level=DEBUG
diff --git a/utils/testnglibs/byte-buddy-1.10.15.jar b/utils/testnglibs/byte-buddy-1.10.15.jar
new file mode 100644 (file)
index 0000000..ff61903
Binary files /dev/null and b/utils/testnglibs/byte-buddy-1.10.15.jar differ
diff --git a/utils/testnglibs/byte-buddy-agent-1.10.15.jar b/utils/testnglibs/byte-buddy-agent-1.10.15.jar
new file mode 100644 (file)
index 0000000..fd181bb
Binary files /dev/null and b/utils/testnglibs/byte-buddy-agent-1.10.15.jar differ
diff --git a/utils/testnglibs/mockito-core-3.6.0.jar b/utils/testnglibs/mockito-core-3.6.0.jar
new file mode 100644 (file)
index 0000000..fd004cc
Binary files /dev/null and b/utils/testnglibs/mockito-core-3.6.0.jar differ
diff --git a/utils/testnglibs/objenesis-3.1.jar b/utils/testnglibs/objenesis-3.1.jar
new file mode 100644 (file)
index 0000000..e6be642
Binary files /dev/null and b/utils/testnglibs/objenesis-3.1.jar differ