reconciled with applet
authorBobHanson <hansonr@stolaf.edu>
Wed, 8 Apr 2020 19:16:11 +0000 (14:16 -0500)
committerBobHanson <hansonr@stolaf.edu>
Wed, 8 Apr 2020 19:16:11 +0000 (14:16 -0500)
src/jalview/bin/ApplicationSingletonProvider.java [new file with mode: 0644]
src/jalview/bin/Cache.java
src/jalview/gui/Preferences.java
src/jalview/io/BackupFiles.java
src/jalview/jbgui/GPreferences.java

diff --git a/src/jalview/bin/ApplicationSingletonProvider.java b/src/jalview/bin/ApplicationSingletonProvider.java
new file mode 100644 (file)
index 0000000..1b90259
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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 jalview.util.Platform;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class to hold singleton objects, whose scope (context) is
+ * <ul>
+ * <li>the Java runtime (JVM) when running as Java</li>
+ * <li>one 'applet', when running as JalviewJS</li>
+ * </ul>
+ * This allows separation of multiple JS applets running on the same browser
+ * page, each with their own 'singleton' instances.
+ * <p>
+ * Instance objects are held in a separate Map (keyed by Class) for each
+ * context. For Java, this is just a single static Map. For SwingJS, the map is
+ * stored as a field {@code _swingjsSingletons} of
+ * {@code Thread.currentThread.getThreadGroup()}, as a proxy for the applet.
+ * <p>
+ * Note that when an applet is stopped, its ThreadGroup is removed, allowing any
+ * singleton references to be garbage collected.
+ * 
+ * @author hansonr
+ */
+public class ApplicationSingletonProvider
+{
+  /**
+   * A tagging interface to mark classes whose singleton instances may be served
+   * by {@code ApplicationSingletonProvider}, giving a distinct instance per JS
+   * 'applet'.
+   * <p>
+   * A class whose singleton should have global scope (be shared across all
+   * applets on a page) should <em>not</em> use this mechanism, but just provide
+   * a single instance (class static member) in the normal way.
+   */
+  public interface ApplicationSingletonI
+  {
+  }
+  
+  /*
+   * Map used to hold singletons in JVM context
+   */
+  private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> singletons = new HashMap<>();
+
+  /**
+   * private constructor for non-instantiable class
+   */
+  private ApplicationSingletonProvider()
+  {
+  }
+
+  /**
+   * Returns the singletons map for the current context (JVM for Java,
+   * ThreadGroup for JS), creating the map on the first request for each JS
+   * ThreadGroup
+   * 
+   * @return
+   */
+  @SuppressWarnings("unchecked")
+  private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> getContextMap()
+  {
+    return (Platform.isJS()
+            ? (Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI>) Platform
+                    .getJSSingletons()
+            : singletons);
+  }
+
+  /**
+   * Answers the singleton instance of the given class for the current context
+   * (JVM or SwingJS 'applet'). If no instance yet exists, one is created, by
+   * calling the class's no-argument constructor. Answers null if any error
+   * occurs (or occurred previously for the same class).
+   * 
+   * @param c
+   * @return
+   */
+  public static ApplicationSingletonI getInstance(Class<? extends ApplicationSingletonI> c)
+  {
+    Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();
+    if (map.containsKey(c))
+    {
+      /*
+       * singleton already created _or_ creation failed (null value stored)
+       */
+      return map.get(c);
+    }
+
+    /*
+     * create and save the singleton
+     */
+    ApplicationSingletonI o = map.get(c);
+    try
+    {
+      Constructor<? extends ApplicationSingletonI> con = c
+              .getDeclaredConstructor();
+      con.setAccessible(true);
+      o = con.newInstance();
+    } catch (IllegalAccessException | InstantiationException
+            | IllegalArgumentException | InvocationTargetException
+            | NoSuchMethodException | SecurityException e)
+    {
+      Cache.log.error("Failed to create singleton for " + c.toString()
+              + ", error was: " + e.toString());
+      e.printStackTrace();
+    }
+
+    /*
+     * store the new singleton; note that a
+     * null value is saved if construction failed
+     */
+    getContextMap().put(c, o);
+    return o;
+  }
+
+  /**
+   * Removes the current singleton instance of the given class from the current
+   * application context. This has the effect of ensuring that a new instance is
+   * created the next time one is requested.
+   * 
+   * @param c
+   */
+  public static void removeInstance(
+          Class<? extends ApplicationSingletonI> c)
+  {
+    Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();
+    if (map != null)
+    {
+      map.remove(c);
+    }
+  }
+}
index 788cb02..04add1f 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.awt.Dimension;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
@@ -54,6 +44,18 @@ import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import org.apache.log4j.SimpleLayout;
 
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+import jalview.datamodel.PDBEntry;
+import jalview.gui.Preferences;
+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;
+
 /**
  * Stores and retrieves Jalview Application Properties Lists and fields within
  * list entries are separated by '|' symbols unless otherwise stated (|) clauses
@@ -208,8 +210,26 @@ import org.apache.log4j.SimpleLayout;
  * @author $author$
  * @version $Revision$
  */
-public class Cache
+public class Cache implements ApplicationSingletonI
 {
+
+  private Cache()
+  {
+    // private singleton
+  }
+
+  /**
+   * In Java, this will be a static field instance, which will be
+   * application-specific; in JavaScript it will be an applet-specific instance
+   * tied to the applet's ThreadGroup.
+   * 
+   * @return
+   */
+  public static Cache getInstance()
+  {
+    return (Cache) ApplicationSingletonProvider.getInstance(Cache.class);
+  }
+
   /**
    * property giving log4j level for CASTOR loggers
    */
@@ -266,7 +286,9 @@ public class Cache
   public static Logger log;
 
   /** Jalview Properties */
-  public static Properties applicationProperties = new Properties()
+  // BH 2019.05.08 was static
+  @SuppressWarnings("serial")
+  public Properties applicationProperties = new Properties()
   {
     // override results in properties output in alphabetical order
     @Override
@@ -277,7 +299,8 @@ public class Cache
   };
 
   /** Default file is ~/.jalview_properties */
-  static String propertiesFile;
+  // BH 2020 private, not static
+  private String propertiesFile;
 
   private static boolean propsAreReadOnly = Platform.isJS();
 
@@ -333,17 +356,22 @@ public class Cache
    */
   public static void loadProperties(String propsFile)
   {
+    getInstance().loadPropertiesImpl(propsFile);
+  }
+
+  private void loadPropertiesImpl(String propsFile)
+  {
     propertiesFile = propsFile;
     if (propsFile == null && !propsAreReadOnly)
     {
       propertiesFile = System.getProperty("user.home") + File.separatorChar
               + ".jalview_properties";
-    }
-    else
-    {
-      // don't corrupt the file we've been given.
-      propsAreReadOnly = true;
-    }
+      }
+      else
+      {
+        // don't corrupt the file we've been given.
+        propsAreReadOnly = true;
+      }
 
     if (propertiesFile == null)
     { // BH 2019
@@ -397,7 +425,8 @@ public class Cache
     }
 
     // LOAD THE AUTHORS FROM THE authors.props file
-    String authorDetails = resolveResourceURLFor("/authors.props");
+    String authorDetails = (Platform.isJS() ? null
+            : resolveResourceURLFor("/authors.props"));
 
     try
     {
@@ -415,9 +444,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);
@@ -436,12 +465,12 @@ public class Cache
             jalview.bin.Cache.getDefault("sifts_cache_threshold_in_days",
                     DEFAULT_CACHE_THRESHOLD_IN_DAYS));
 
-    IdOrgSettings.setUrl(getDefault("ID_ORG_HOSTURL",
+    IdOrgSettings.setUrl(getDefault(Preferences.ID_ORG_HOSTURL,
             "http://www.jalview.org/services/identifiers"));
     IdOrgSettings.setDownloadLocation(ID_ORG_FILE);
 
     StructureImportSettings.setDefaultStructureFileFormat(jalview.bin.Cache
-            .getDefault("PDB_DOWNLOAD_FORMAT", PDB_DOWNLOAD_FORMAT));
+            .getDefault(Preferences.PDB_DOWNLOAD_FORMAT, PDB_DOWNLOAD_FORMAT));
     StructureImportSettings
             .setDefaultPDBFileParser(DEFAULT_PDB_FILE_PARSER);
     // StructureImportSettings
@@ -526,55 +555,55 @@ public class Cache
             false);
   }
 
-  /**
-   * construct a resource URL for the given absolute resource pathname
-   * 
-   * @param resourcePath
-   * @return
-   */
-  private static String resolveResourceURLFor(String resourcePath)
-  {
-    String url = null;
-    if (Platform.isJS() || !Cache.class.getProtectionDomain()
-            .getCodeSource().getLocation().toString().endsWith(".jar"))
+    /**
+     * construct a resource URL for the given absolute resource pathname
+     * 
+     * @param resourcePath
+     * @return
+     */
+    private static String resolveResourceURLFor(String resourcePath)
     {
-      try
+      String url = null;
+      if (Platform.isJS() || !Cache.class.getProtectionDomain()
+              .getCodeSource().getLocation().toString().endsWith(".jar"))
       {
-        url = Cache.class.getResource(resourcePath).toString();
-      } catch (Exception ex)
+        try
+        {
+          url = Cache.class.getResource(resourcePath).toString();
+        } catch (Exception ex)
+        {
+          System.err.println("Failed to resolve resource " + resourcePath + ": "
+                  + ex.getMessage());
+        }
+      }
+      else
       {
-        System.err.println("Failed to resolve resource " + resourcePath + ": "
-                + ex.getMessage());
+        url = "jar:".concat(Cache.class.getProtectionDomain().getCodeSource()
+                .getLocation().toString().concat("!" + resourcePath));
       }
+      return url;
     }
-    else
-    {
-      url = "jar:".concat(Cache.class.getProtectionDomain().getCodeSource()
-              .getLocation().toString().concat("!" + resourcePath));
-    }
-    return url;
-  }
 
-  public static void loadBuildProperties(boolean reportVersion)
-  {
-    String codeInstallation = getProperty("INSTALLATION");
-    boolean printVersion = codeInstallation == null;
-
-    /*
-     * read build properties - from the Jalview jar for a Java distribution,
-     * or from codebase file in test or JalviewJS context
-     */
-    try
+    public static void loadBuildProperties(boolean reportVersion)
     {
+      String codeInstallation = getProperty("INSTALLATION");
+      boolean printVersion = codeInstallation == null;
+
+      /*
+       * read build properties - from the Jalview jar for a Java distribution,
+       * or from codebase file in test or JalviewJS context
+       */
+      try
+      {
       String buildDetails = resolveResourceURLFor("/.build_properties");
       URL localJarFileURL = new URL(buildDetails);
-      InputStream in = localJarFileURL.openStream();
-      applicationProperties.load(in);
-      in.close();
-    } catch (Exception ex)
-    {
-      System.out.println("Error reading build details: " + ex);
-      applicationProperties.remove("VERSION");
+        InputStream in = localJarFileURL.openStream();
+        getInstance().applicationProperties.load(in);
+        in.close();
+      } catch (Exception ex)
+      {
+        System.out.println("Error reading build details: " + ex);
+        getInstance().applicationProperties.remove("VERSION");
     }
     String codeVersion = getProperty("VERSION");
     codeInstallation = getProperty("INSTALLATION");
@@ -598,7 +627,8 @@ public class Cache
     }
   }
 
-  private static void deleteBuildProperties()
+  // BH 2020 was static  
+  private void deleteBuildProperties()
   {
     applicationProperties.remove("LATEST_VERSION");
     applicationProperties.remove("VERSION");
@@ -620,31 +650,47 @@ public class Cache
    */
   public static String getProperty(String key)
   {
-    String prop = applicationProperties.getProperty(key);
-    if (prop == null && Platform.isJS())
-    {
-      prop = applicationProperties.getProperty(Platform.getUniqueAppletID()
-              + "_" + JS_PROPERTY_PREFIX + key);
-    }
-    return prop;
+    return getInstance().applicationProperties.getProperty(key);
+
+// old, original idea:
+//    String prop = applicationProperties.getProperty(key);
+//    if (prop == null && Platform.isJS())
+//    {
+//      prop = applicationProperties.getProperty(Platform.getUniqueAppletID()
+//              + "_" + JS_PROPERTY_PREFIX + key);
+//    }
+//    return prop;
+
   }
 
+  
+  //These methods are used when checking if the saved preference is different
+  // to the default setting 
+  
   /**
-   * These methods are used when checking if the saved preference is different
-   * to the default setting
+   * Returns the boolean property value for the given property name. If the
+   * value is absent then the default value is returned instead.
+   * 
+   * @param property
+   * @param def
+   * @return
    */
-
   public static boolean getDefault(String property, boolean def)
   {
     String string = getProperty(property);
-    if (string != null)
-    {
-      def = Boolean.valueOf(string).booleanValue();
-    }
-
-    return def;
+    // BH simplification only    
+    return string == null ? def : Boolean.parseBoolean(string);
   }
 
+  /**
+   * Returns the integer property value for the given property name. If the
+   * value is absent, or malformed (not an integer), then the default value is
+   * returned instead.
+   * 
+   * @param property
+   * @param def
+   * @return
+   */
   public static int getDefault(String property, int def)
   {
     String string = getProperty(property);
@@ -652,7 +698,9 @@ public class Cache
     {
       try
       {
-        def = Integer.parseInt(string);
+          // BH simplification only
+          
+        return Integer.parseInt(string);
       } catch (NumberFormatException e)
       {
         System.out.println("Error parsing int property '" + property
@@ -664,8 +712,12 @@ public class Cache
   }
 
   /**
-   * Answers the value of the given property, or the supplied default value if
+   * Returns the value of the given property, or the supplied default value if
    * the property is not set
+   * 
+   * @param property
+   * @param def
+   * @return
    */
   public static String getDefault(String property, String def)
   {
@@ -685,11 +737,44 @@ public class Cache
    */
   public static Object setProperty(String key, String obj)
   {
+    return getInstance().setPropertyImpl(key, obj, true);
+  }
+
+  /**
+   * 
+   * Sets a property value for the running application, without saving it to the
+   * properties file. 
+   * 
+   * Replaces direct call to Cache.applicationProperties.setProperty
+   * 
+   * Used extensively in AppletParams and Preferences.ok_actionPerformed
+   * 
+   * 
+   * @param key
+   * @param obj
+   */
+  public static void setPropertyNoSave(String key, String obj)
+  {
+    getInstance().setPropertyImpl(key, obj, false);
+  }
+
+
+  /**
+   * Sets a property value, and optionally also saves the current properties to
+   * file
+   * 
+   * @param key
+   * @param obj
+   * @param andSave
+   * @return
+   */
+  private Object setPropertyImpl(String key, String obj, boolean andSave)
+  {
     Object oldValue = null;
     try
     {
       oldValue = applicationProperties.setProperty(key, obj);
-      if (propertiesFile != null && !propsAreReadOnly)
+      if (andSave && !propsAreReadOnly && propertiesFile != null)
       {
         FileOutputStream out = new FileOutputStream(propertiesFile);
         applicationProperties.store(out, "---JalviewX Properties File---");
@@ -704,21 +789,58 @@ public class Cache
   }
 
   /**
-   * remove the specified property from the jalview properties file
+   * Removes the specified property from the jalview properties file
    * 
-   * @param string
+   * @param key
    */
-  public static void removeProperty(String string)
+  public static void removeProperty(String key)
   {
-    applicationProperties.remove(string);
-    saveProperties();
+    getInstance().removePropertyImpl(key, true);
   }
 
   /**
-   * save the properties to the jalview properties path
+   * Removes the named property for the running application, without saving the
+   * properties file.
+   * 
+   * Called by Preferences.ok_actionPerformed specifically for SEQUENCE_LINKS and STORED_LINKS. 
+   * 
+   * 
+   * @param key
+   */
+  public static void removePropertyNoSave(String key)
+  {
+    getInstance().removePropertyImpl(key, false);
+  }
+
+  /**
+   * Removes the named property, and optionally saves the current properties to
+   * file
+   * 
+   * @param key
+   * @param andSave
+   */
+  private void removePropertyImpl(String key, boolean andSave)
+  {
+    applicationProperties.remove(key);
+    if (andSave)
+    {
+      savePropertiesImpl();
+    }
+  }
+
+  /**
+   * Saves the current properties to file
    */
   public static void saveProperties()
   {
+    getInstance().savePropertiesImpl();
+  }
+
+  /**
+   * save the properties to the jalview properties path
+   */
+  private void savePropertiesImpl()
+  {
     if (!propsAreReadOnly)
     {
       try
@@ -733,10 +855,17 @@ public class Cache
     }
   }
 
+  private final static int UNTESTED = -1;
+
+  private final static int TRUE = 1;
+
+  private final static int FALSE = 0;
+
   /**
    * internal vamsas class discovery state
    */
-  private static int vamsasJarsArePresent = -1;
+  private static int vamsasJarsArePresent = (Platform.isJS() ? FALSE
+          : UNTESTED);
 
   /**
    * Searches for vamsas client classes on class path.
@@ -745,7 +874,7 @@ public class Cache
    */
   public static boolean vamsasJarsPresent()
   {
-    if (vamsasJarsArePresent == -1)
+    if (vamsasJarsArePresent == UNTESTED)
     {
       try
       {
@@ -754,7 +883,7 @@ public class Cache
         {
           jalview.bin.Cache.log.debug(
                   "Found Vamsas Classes (uk.ac..vamsas.client.VorbaId can be loaded)");
-          vamsasJarsArePresent = 1;
+          vamsasJarsArePresent = TRUE;
           Logger lvclient = Logger.getLogger("uk.ac.vamsas");
           lvclient.setLevel(Level.toLevel(Cache
                   .getDefault("logs.Vamsas.Level", Level.INFO.toString())));
@@ -765,17 +894,18 @@ public class Cache
         }
       } catch (Exception e)
       {
-        vamsasJarsArePresent = 0;
+        vamsasJarsArePresent = FALSE;
         jalview.bin.Cache.log.debug("Vamsas Classes are not present");
       }
     }
-    return (vamsasJarsArePresent > 0);
+    return (vamsasJarsArePresent == TRUE);
   }
 
   /**
    * internal vamsas class discovery state
    */
-  private static int groovyJarsArePresent = -1;
+  private static int groovyJarsArePresent = (Platform.isJS() ? FALSE
+          : UNTESTED);
 
   /**
    * Searches for vamsas client classes on class path.
@@ -784,7 +914,7 @@ public class Cache
    */
   public static boolean groovyJarsPresent()
   {
-    if (groovyJarsArePresent == -1)
+    if (groovyJarsArePresent == UNTESTED)
     {
       try
       {
@@ -793,7 +923,7 @@ public class Cache
         {
           jalview.bin.Cache.log.debug(
                   "Found Groovy (groovy.lang.GroovyObject can be loaded)");
-          groovyJarsArePresent = 1;
+          groovyJarsArePresent = TRUE;
           Logger lgclient = Logger.getLogger("groovy");
           lgclient.setLevel(Level.toLevel(Cache
                   .getDefault("logs.Groovy.Level", Level.INFO.toString())));
@@ -804,15 +934,15 @@ public class Cache
         }
       } catch (Error e)
       {
-        groovyJarsArePresent = 0;
+        groovyJarsArePresent = FALSE;
         jalview.bin.Cache.log.debug("Groovy Classes are not present", e);
       } catch (Exception e)
       {
-        groovyJarsArePresent = 0;
+        groovyJarsArePresent = FALSE;
         jalview.bin.Cache.log.debug("Groovy Classes are not present");
       }
     }
-    return (groovyJarsArePresent > 0);
+    return (groovyJarsArePresent == TRUE);
   }
 
   /**
@@ -821,15 +951,18 @@ public class Cache
    */
   protected static Object tracker = null;
 
-  protected static Class trackerfocus = null;
+  protected static Class<?> trackerfocus = null;
 
-  protected static Class jgoogleanalyticstracker = null;
+  protected static Class<?> jgoogleanalyticstracker = null;
 
   /**
    * Initialise the google tracker if it is not done already.
    */
   public static void initGoogleTracker()
   {
+
+    // TODO: SwingJS JavaScript tracker?
+
     if (tracker == null)
     {
       if (jgoogleanalyticstracker == null)
@@ -942,7 +1075,10 @@ public class Cache
   }
 
   /**
-   * get the user's default colour if available
+   * Returns the Color value of the given property's value, or the supplied
+   * default colour if no property is found, or it cannot be parsed to a colour.
+   * Colours are normally saved as hex rgb values, though other formats may be
+   * parseable.
    * 
    * @param property
    * @param defcolour
@@ -965,6 +1101,22 @@ public class Cache
   }
 
   /**
+   * Stores a colour as a Jalview property, converted to hex values for rgb.
+   * Properties are not saved to file.
+   * 
+   * 
+   * 
+   * Used extensively in Preferences.ok_actionPerformed
+   * 
+   * @param property
+   * @param colour
+   */
+  public static void setColourPropertyNoSave(String property, Color colour)
+  {
+    setPropertyNoSave(property, jalview.util.Format.getHexString(colour));
+  }
+
+  /**
    * store a colour as a Jalview user default property
    * 
    * @param property
@@ -977,6 +1129,7 @@ public class Cache
 
   /**
    * Stores a formatted date in a jalview property, using a fixed locale.
+   * Updated properties are written out to the properties file.
    * 
    * @param propertyName
    * @param date
@@ -1053,11 +1206,11 @@ public class Cache
     }
     if (value == null || value.trim().length() < 1)
     {
-      Cache.applicationProperties.remove(propName);
+      getInstance().applicationProperties.remove(propName);
     }
     else
     {
-      Cache.applicationProperties.setProperty(propName, value);
+      getInstance().applicationProperties.setProperty(propName, value);
     }
   }
 
@@ -1102,13 +1255,15 @@ public class Cache
     {
       if (coloursFound.toString().length() > 1)
       {
-        setProperty(UserDefinedColours.USER_DEFINED_COLOURS,
+        // BH moved to Preferences for consistency
+        setProperty(Preferences.USER_DEFINED_COLOURS,
                 coloursFound.toString());
       }
       else
       {
-        applicationProperties
-                .remove(UserDefinedColours.USER_DEFINED_COLOURS);
+        // BH moved to Preferences for consistency
+        getInstance().applicationProperties
+                .remove(Preferences.USER_DEFINED_COLOURS);
       }
     }
   }
@@ -1174,4 +1329,40 @@ public class Cache
     // eg 'built from Source' or update channel
     return jalview.bin.Cache.getDefault("INSTALLATION", "unknown");
   }
+
+  /**
+   * retrieve a dimension, such as for Jmol
+   * 
+   * @param property
+   * @param def
+   * @return
+   */
+  public static Dimension getDefaultDim(String property, Dimension def)
+  {
+    String s = getProperty(property);
+    if (s != null)
+    {
+      if (s.indexOf(',') < 0)
+      {
+        s = s.trim().replace(' ', ',');
+        if (s.indexOf(',') < 0)
+        {
+          s += "," + s;
+        }
+      }
+      try
+      {
+        int pt = s.indexOf(",");
+        return new Dimension(Integer.parseInt(s.substring(0, pt)),
+                Integer.parseInt(s.substring(pt + 1)));
+      } catch (NumberFormatException e)
+      {
+        System.out.println("Error parsing Dimension property '" + property
+                + "' with value '" + s + "'");
+      }
+    }
+    return def;
+  }
+
+
 }
index 04b83a3..6d46ce7 100755 (executable)
@@ -38,6 +38,7 @@ import jalview.urls.UrlLinkTableModel;
 import jalview.urls.api.UrlProviderFactoryI;
 import jalview.urls.api.UrlProviderI;
 import jalview.urls.desktop.DesktopUrlProviderFactory;
+import jalview.util.BrowserLauncher;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.UrlConstants;
@@ -81,8 +82,12 @@ import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
  * @author $author$
  * @version $Revision$
  */
+@SuppressWarnings("serial")
 public class Preferences extends GPreferences
 {
+
+  // original to develop
+
   public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
 
   public static final String SCALE_PROTEIN_TO_CDNA = "SCALE_PROTEIN_TO_CDNA";
@@ -123,13 +128,119 @@ public class Preferences extends GPreferences
 
   private static final int MAX_FONT_SIZE = 30;
 
+  // new for applet
+
+  public static final String ALLOW_UNPUBLISHED_PDB_QUERYING = "ALLOW_UNPUBLISHED_PDB_QUERYING";
+
+  public static final String ANNOTATIONCOLOUR_MAX = "ANNOTATIONCOLOUR_MAX";
+
+  public static final String ANNOTATIONCOLOUR_MIN = "ANNOTATIONCOLOUR_MIN";
+
+  public static final String ANTI_ALIAS = "ANTI_ALIAS";
+
+  public static final String AUTO_CALC_CONSENSUS = "AUTO_CALC_CONSENSUS";
+
+  public static final String AUTOASSOCIATE_PDBANDSEQS = "AUTOASSOCIATE_PDBANDSEQS";
+
+  public static final String BLOSUM62_PCA_FOR_NUCLEOTIDE = "BLOSUM62_PCA_FOR_NUCLEOTIDE";
+
+  public static final String CENTRE_COLUMN_LABELS = "CENTRE_COLUMN_LABELS";
+
+  public static final String DBREFFETCH_USEPICR = "DBREFFETCH_USEPICR";
+
+  public static final String FIGURE_AUTOIDWIDTH = "FIGURE_AUTOIDWIDTH";
+
+  public static final String FIGURE_FIXEDIDWIDTH = "FIGURE_FIXEDIDWIDTH";
+
+  public static final String FOLLOW_SELECTIONS = "FOLLOW_SELECTIONS";
+
+  public static final String FONT_NAME = "FONT_NAME";
+
+  public static final String FONT_SIZE = "FONT_SIZE";
+
+  public static final String FONT_STYLE = "FONT_STYLE";
+
+  public static final String GAP_SYMBOL = "GAP_SYMBOL";
+
+  public static final String HIDE_INTRONS = "HIDE_INTRONS";
+
+  public static final String ID_ITALICS = "ID_ITALICS";
+
+  public static final String ID_ORG_HOSTURL = "ID_ORG_HOSTURL";
+
+  public static final String MAP_WITH_SIFTS = "MAP_WITH_SIFTS";
+
+  public static final String NOQUESTIONNAIRES = "NOQUESTIONNAIRES";
+
+  public static final String NORMALISE_CONSENSUS_LOGO = "NORMALISE_CONSENSUS_LOGO";
+
+  public static final String NORMALISE_LOGO = "NORMALISE_LOGO";
+
+  public static final String PAD_GAPS = "PAD_GAPS";
+
+  public static final String PDB_DOWNLOAD_FORMAT = "PDB_DOWNLOAD_FORMAT";
+
+  public static final String QUESTIONNAIRE = "QUESTIONNAIRE";
+
+  public static final String RELAXEDSEQIDMATCHING = "RELAXEDSEQIDMATCHING";
+
+  public static final String RIGHT_ALIGN_IDS = "RIGHT_ALIGN_IDS";
+
+  public static final String SHOW_ANNOTATIONS = "SHOW_ANNOTATIONS";
+
+  public static final String SHOW_CONSENSUS = "SHOW_CONSENSUS";
+
+  public static final String SHOW_CONSENSUS_HISTOGRAM = "SHOW_CONSENSUS_HISTOGRAM";
+
+  public static final String SHOW_CONSENSUS_LOGO = "SHOW_CONSENSUS_LOGO";
+
+  public static final String SHOW_CONSERVATION = "SHOW_CONSERVATION";
+
+  public static final String SHOW_DBREFS_TOOLTIP = "SHOW_DBREFS_TOOLTIP";
+
+  public static final String SHOW_GROUP_CONSENSUS = "SHOW_GROUP_CONSENSUS";
+
+  public static final String SHOW_GROUP_CONSERVATION = "SHOW_GROUP_CONSERVATION";
+
+  public static final String SHOW_IDENTITY = "SHOW_IDENTITY";
+
+  public static final String SHOW_FULLSCREEN = "SHOW_FULLSCREEN";
+
+  public static final String SHOW_JVSUFFIX = "SHOW_JVSUFFIX";
+
+  public static final String SHOW_NPFEATS_TOOLTIP = "SHOW_NPFEATS_TOOLTIP";
+
+  public static final String SHOW_OVERVIEW = "SHOW_OVERVIEW";
+
+  public static final String SHOW_QUALITY = "SHOW_QUALITY";
+
+  public static final String SHOW_UNCONSERVED = "SHOW_UNCONSERVED";
+
+  public static final String SORT_ALIGNMENT = "SORT_ALIGNMENT";
+
+  public static final String SORT_BY_TREE = "SORT_BY_TREE";
+
+  public static final String STRUCTURE_DIMENSIONS = "STRUCTURE_DIMENSIONS";
+
+  public static final String UNIPROT_DOMAIN = "UNIPROT_DOMAIN";
+
+  public static final String USE_FULL_SO = "USE_FULL_SO";
+
+  public static final String USER_DEFINED_COLOURS = "USER_DEFINED_COLOURS";
+
+  public static final String WRAP_ALIGNMENT = "WRAP_ALIGNMENT";
+
+
+  public static final Dimension DEFAULT_STRUCTURE_DIMENSIONS = new Dimension(
+          600, 600);
+
   /**
    * Holds name and link separated with | character. Sequence ID must be
    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
    */
-  public static UrlProviderI sequenceUrlLinks;
+  public static UrlProviderI sequenceUrlLinks; // must be nonfinal for test
 
-  public static UrlLinkTableModel dataModel;
+  public final static UrlLinkTableModel dataModel;
 
   /**
    * Holds name and link separated with | character. Sequence IDS and Sequences
@@ -139,7 +250,8 @@ public class Preferences extends GPreferences
    * (TODO: proper escape for using | to separate ids or sequences
    */
 
-  public static List<String> groupURLLinks;
+  public static final List<String> groupURLLinks; // not implemented
+
   static
   {
     // get links selected to be in the menu (SEQUENCE_LINKS)
@@ -165,7 +277,7 @@ public class Preferences extends GPreferences
      * .properties file as '|' separated strings
      */
 
-    groupURLLinks = new ArrayList<>();
+    groupURLLinks = new ArrayList<>(); // not implemented
   }
 
   JInternalFrame frame;
@@ -200,11 +312,16 @@ public class Preferences extends GPreferences
       wsPrefs = new WsPreferences();
       wsTab.add(wsPrefs, BorderLayout.CENTER);
     }
-    int width = 500, height = 450;
+    int width = 700, height = 510; // BH 2019.07.12 added 60 to height and 40 to
+                                   // width (for Visual check boxes and Links
+                                   // "Double Click" header)
+    // BH 2019.09.24 added 20 to width
+    // (structure panel was too small anyway, and I added a default dimension
+    // for Jmol
     if (Platform.isAMacAndNotJS())
     {
       width = 570;
-      height = 480;
+      height = 540; // BH 2019.07.12 added 30
     }
 
     Desktop.addInternalFrame(frame,
@@ -214,30 +331,30 @@ public class Preferences extends GPreferences
     /*
      * Set Visual tab defaults
      */
-    seqLimit.setSelected(Cache.getDefault("SHOW_JVSUFFIX", true));
-    rightAlign.setSelected(Cache.getDefault("RIGHT_ALIGN_IDS", false));
-    fullScreen.setSelected(Cache.getDefault("SHOW_FULLSCREEN", false));
-    annotations.setSelected(Cache.getDefault("SHOW_ANNOTATIONS", true));
-
-    conservation.setSelected(Cache.getDefault("SHOW_CONSERVATION", true));
-    quality.setSelected(Cache.getDefault("SHOW_QUALITY", true));
-    identity.setSelected(Cache.getDefault("SHOW_IDENTITY", true));
-    openoverv.setSelected(Cache.getDefault("SHOW_OVERVIEW", false));
+    seqLimit.setSelected(Cache.getDefault(SHOW_JVSUFFIX, true));
+    rightAlign.setSelected(Cache.getDefault(RIGHT_ALIGN_IDS, false));
+    fullScreen.setSelected(Cache.getDefault(SHOW_FULLSCREEN, false));
+    annotations.setSelected(Cache.getDefault(SHOW_ANNOTATIONS, true));
+
+    conservation.setSelected(Cache.getDefault(SHOW_CONSERVATION, true));
+    quality.setSelected(Cache.getDefault(SHOW_QUALITY, true));
+    identity.setSelected(Cache.getDefault(SHOW_IDENTITY, true));
+    openoverv.setSelected(Cache.getDefault(SHOW_OVERVIEW, false));
     showUnconserved
-            .setSelected(Cache.getDefault("SHOW_UNCONSERVED", false));
+            .setSelected(Cache.getDefault(SHOW_UNCONSERVED, false));
     showOccupancy.setSelected(Cache.getDefault(SHOW_OCCUPANCY, false));
     showGroupConsensus
-            .setSelected(Cache.getDefault("SHOW_GROUP_CONSENSUS", false));
+            .setSelected(Cache.getDefault(SHOW_GROUP_CONSENSUS, false));
     showGroupConservation.setSelected(
-            Cache.getDefault("SHOW_GROUP_CONSERVATION", false));
+            Cache.getDefault(SHOW_GROUP_CONSERVATION, false));
     showConsensHistogram.setSelected(
-            Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM", true));
+            Cache.getDefault(SHOW_CONSENSUS_HISTOGRAM, true));
     showConsensLogo
-            .setSelected(Cache.getDefault("SHOW_CONSENSUS_LOGO", false));
+            .setSelected(Cache.getDefault(SHOW_CONSENSUS_LOGO, false));
     showNpTooltip
-            .setSelected(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
+            .setSelected(Cache.getDefault(SHOW_NPFEATS_TOOLTIP, true));
     showDbRefTooltip
-            .setSelected(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true));
+            .setSelected(Cache.getDefault(SHOW_DBREFS_TOOLTIP, true));
 
     String[] fonts = java.awt.GraphicsEnvironment
             .getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
@@ -255,28 +372,28 @@ public class Preferences extends GPreferences
     fontStyleCB.addItem("bold");
     fontStyleCB.addItem("italic");
 
-    fontNameCB.setSelectedItem(Cache.getDefault("FONT_NAME", "SansSerif"));
-    fontSizeCB.setSelectedItem(Cache.getDefault("FONT_SIZE", "10"));
+    fontNameCB.setSelectedItem(Cache.getDefault(FONT_NAME, "SansSerif"));
+    fontSizeCB.setSelectedItem(Cache.getDefault(FONT_SIZE, "10"));
     fontStyleCB.setSelectedItem(
-            Cache.getDefault("FONT_STYLE", Font.PLAIN + ""));
+            Cache.getDefault(FONT_STYLE, Font.PLAIN + ""));
 
-    smoothFont.setSelected(Cache.getDefault("ANTI_ALIAS", true));
+    smoothFont.setSelected(Cache.getDefault(ANTI_ALIAS, false));
     scaleProteinToCdna
             .setSelected(Cache.getDefault(SCALE_PROTEIN_TO_CDNA, false));
 
-    idItalics.setSelected(Cache.getDefault("ID_ITALICS", true));
+    idItalics.setSelected(Cache.getDefault(ID_ITALICS, true));
 
-    wrap.setSelected(Cache.getDefault("WRAP_ALIGNMENT", false));
+    wrap.setSelected(Cache.getDefault(WRAP_ALIGNMENT, false));
 
     gapSymbolCB.addItem("-");
     gapSymbolCB.addItem(".");
 
-    gapSymbolCB.setSelectedItem(Cache.getDefault("GAP_SYMBOL", "-"));
+    gapSymbolCB.setSelectedItem(Cache.getDefault(GAP_SYMBOL, "-"));
 
     sortby.addItem("No sort");
     sortby.addItem("Id");
     sortby.addItem("Pairwise Identity");
-    sortby.setSelectedItem(Cache.getDefault("SORT_ALIGNMENT", "No sort"));
+    sortby.setSelectedItem(Cache.getDefault(SORT_ALIGNMENT, "No sort"));
 
     sortAnnBy.addItem(SequenceAnnotationOrder.NONE.toString());
     sortAnnBy
@@ -317,9 +434,9 @@ public class Preferences extends GPreferences
     newProp = Cache.getDefault(DEFAULT_COLOUR_NUC, null);
     nucColour.setSelectedItem(newProp != null ? newProp : oldProp);
     minColour.setBackground(
-            Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN", Color.orange));
+            Cache.getDefaultColour(ANNOTATIONCOLOUR_MIN, Color.orange));
     maxColour.setBackground(
-            Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red));
+            Cache.getDefaultColour(ANNOTATIONCOLOUR_MAX, Color.red));
 
     /*
      * Set overview panel defaults
@@ -349,6 +466,10 @@ public class Preferences extends GPreferences
     addTempFactor.setEnabled(structSelected);
     structViewer.setSelectedItem(
             Cache.getDefault(STRUCTURE_DISPLAY, ViewerType.JMOL.name()));
+    Dimension d = Cache.getDefaultDim(STRUCTURE_DIMENSIONS,
+            DEFAULT_STRUCTURE_DIMENSIONS);
+    String s = d.width + "," + d.height;
+    structureDimensions.setText(s);
     chimeraPath.setText(Cache.getDefault(CHIMERA_PATH, ""));
     chimeraPath.addActionListener(new ActionListener()
     {
@@ -359,7 +480,7 @@ public class Preferences extends GPreferences
       }
     });
 
-    if (Cache.getDefault("MAP_WITH_SIFTS", false))
+    if (Cache.getDefault(MAP_WITH_SIFTS, false))
     {
       siftsMapping.setSelected(true);
     }
@@ -369,7 +490,7 @@ public class Preferences extends GPreferences
     }
 
     SiftsSettings
-            .setMapWithSifts(Cache.getDefault("MAP_WITH_SIFTS", false));
+            .setMapWithSifts(Cache.getDefault(MAP_WITH_SIFTS, false));
 
     /*
      * Set Connections tab defaults
@@ -505,7 +626,7 @@ public class Preferences extends GPreferences
     usagestats.setSelected(Cache.getDefault("USAGESTATS", false));
     // note antisense here: default is true
     questionnaire
-            .setSelected(Cache.getProperty("NOQUESTIONNAIRES") == null);
+            .setSelected(Cache.getProperty(NOQUESTIONNAIRES) == null);
     versioncheck.setSelected(Cache.getDefault("VERSION_CHECK", true));
 
     /*
@@ -514,10 +635,10 @@ public class Preferences extends GPreferences
     setupOutputCombo(epsRendering, "EPS_RENDERING");
     setupOutputCombo(htmlRendering, "HTML_RENDERING");
     setupOutputCombo(svgRendering, "SVG_RENDERING");
-    autoIdWidth.setSelected(Cache.getDefault("FIGURE_AUTOIDWIDTH", false));
+    autoIdWidth.setSelected(Cache.getDefault(FIGURE_AUTOIDWIDTH, false));
     userIdWidth.setEnabled(!autoIdWidth.isSelected());
     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
-    Integer wi = Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH");
+    Integer wi = Cache.getIntegerProperty(FIGURE_FIXEDIDWIDTH);
     userIdWidth.setText(wi == null ? "" : wi.toString());
     // TODO: refactor to use common enum via FormatAdapter and allow extension
     // for new flat file formats
@@ -536,9 +657,9 @@ public class Preferences extends GPreferences
      * Set Editing tab defaults
      */
     autoCalculateConsCheck
-            .setSelected(Cache.getDefault("AUTO_CALC_CONSENSUS", true));
-    padGaps.setSelected(Cache.getDefault("PAD_GAPS", false));
-    sortByTree.setSelected(Cache.getDefault("SORT_BY_TREE", false));
+            .setSelected(Cache.getDefault(AUTO_CALC_CONSENSUS, true));
+    padGaps.setSelected(Cache.getDefault(PAD_GAPS, false));
+    sortByTree.setSelected(Cache.getDefault(SORT_BY_TREE, false));
 
     annotations_actionPerformed(null); // update the display of the annotation
                                        // settings
@@ -547,7 +668,11 @@ public class Preferences extends GPreferences
     /*
      * Set Backups tab defaults
      */
-    loadLastSavedBackupsOptions();
+    if (!Platform.isJS())
+    {
+      loadLastSavedBackupsOptions();
+    }
+
   }
 
   /**
@@ -600,65 +725,64 @@ public class Preferences extends GPreferences
     /*
      * Save Visual settings
      */
-    Cache.applicationProperties.setProperty("SHOW_JVSUFFIX",
+    Cache.setPropertyNoSave(SHOW_JVSUFFIX,
             Boolean.toString(seqLimit.isSelected()));
-    Cache.applicationProperties.setProperty("RIGHT_ALIGN_IDS",
+    Cache.setPropertyNoSave(RIGHT_ALIGN_IDS,
             Boolean.toString(rightAlign.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_FULLSCREEN",
+    Cache.setPropertyNoSave(SHOW_FULLSCREEN,
             Boolean.toString(fullScreen.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_OVERVIEW",
+    Cache.setPropertyNoSave(SHOW_OVERVIEW,
             Boolean.toString(openoverv.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
+    Cache.setPropertyNoSave(SHOW_ANNOTATIONS,
             Boolean.toString(annotations.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+    Cache.setPropertyNoSave(SHOW_CONSERVATION,
             Boolean.toString(conservation.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_QUALITY",
+    Cache.setPropertyNoSave(SHOW_QUALITY,
             Boolean.toString(quality.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave(SHOW_IDENTITY,
             Boolean.toString(identity.isSelected()));
 
-    Cache.applicationProperties.setProperty("GAP_SYMBOL",
+    Cache.setPropertyNoSave(GAP_SYMBOL,
             gapSymbolCB.getSelectedItem().toString());
 
-    Cache.applicationProperties.setProperty("FONT_NAME",
+    Cache.setPropertyNoSave(FONT_NAME,
             fontNameCB.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty("FONT_STYLE",
+    Cache.setPropertyNoSave(FONT_STYLE,
             fontStyleCB.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty("FONT_SIZE",
+    Cache.setPropertyNoSave(FONT_SIZE,
             fontSizeCB.getSelectedItem().toString());
 
-    Cache.applicationProperties.setProperty("ID_ITALICS",
+    Cache.setPropertyNoSave(ID_ITALICS,
             Boolean.toString(idItalics.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_UNCONSERVED",
+    Cache.setPropertyNoSave(SHOW_UNCONSERVED,
             Boolean.toString(showUnconserved.isSelected()));
-    Cache.applicationProperties.setProperty(SHOW_OCCUPANCY,
+    Cache.setPropertyNoSave(SHOW_OCCUPANCY,
             Boolean.toString(showOccupancy.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_GROUP_CONSENSUS",
+    Cache.setPropertyNoSave(SHOW_GROUP_CONSENSUS,
             Boolean.toString(showGroupConsensus.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_GROUP_CONSERVATION",
+    Cache.setPropertyNoSave(SHOW_GROUP_CONSERVATION,
             Boolean.toString(showGroupConservation.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSENSUS_HISTOGRAM",
+    Cache.setPropertyNoSave(SHOW_CONSENSUS_HISTOGRAM,
             Boolean.toString(showConsensHistogram.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSENSUS_LOGO",
+    Cache.setPropertyNoSave(SHOW_CONSENSUS_LOGO,
             Boolean.toString(showConsensLogo.isSelected()));
-    Cache.applicationProperties.setProperty("ANTI_ALIAS",
+    Cache.setPropertyNoSave(ANTI_ALIAS,
             Boolean.toString(smoothFont.isSelected()));
-    Cache.applicationProperties.setProperty(SCALE_PROTEIN_TO_CDNA,
+    Cache.setPropertyNoSave(SCALE_PROTEIN_TO_CDNA,
             Boolean.toString(scaleProteinToCdna.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_NPFEATS_TOOLTIP",
+    Cache.setPropertyNoSave(SHOW_NPFEATS_TOOLTIP,
             Boolean.toString(showNpTooltip.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_DBREFS_TOOLTIP",
+    Cache.setPropertyNoSave(SHOW_DBREFS_TOOLTIP,
             Boolean.toString(showDbRefTooltip.isSelected()));
 
-    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT",
+    Cache.setPropertyNoSave(WRAP_ALIGNMENT,
             Boolean.toString(wrap.isSelected()));
 
-    Cache.applicationProperties.setProperty("STARTUP_FILE",
-            startupFileTextfield.getText());
-    Cache.applicationProperties.setProperty("SHOW_STARTUP_FILE",
+    Cache.setPropertyNoSave("STARTUP_FILE", startupFileTextfield.getText());
+    Cache.setPropertyNoSave("SHOW_STARTUP_FILE",
             Boolean.toString(startupCheckbox.isSelected()));
 
-    Cache.applicationProperties.setProperty("SORT_ALIGNMENT",
+    Cache.setPropertyNoSave(SORT_ALIGNMENT,
             sortby.getSelectedItem().toString());
 
     // convert description of sort order to enum name for save
@@ -666,98 +790,102 @@ public class Preferences extends GPreferences
             .forDescription(sortAnnBy.getSelectedItem().toString());
     if (annSortOrder != null)
     {
-      Cache.applicationProperties.setProperty(SORT_ANNOTATIONS,
-              annSortOrder.name());
+      Cache.setPropertyNoSave(SORT_ANNOTATIONS, annSortOrder.name());
     }
 
     final boolean showAutocalcFirst = sortAutocalc.getSelectedIndex() == 0;
-    Cache.applicationProperties.setProperty(SHOW_AUTOCALC_ABOVE,
+    Cache.setPropertyNoSave(SHOW_AUTOCALC_ABOVE,
             Boolean.valueOf(showAutocalcFirst).toString());
 
     /*
      * Save Colours settings
      */
-    Cache.applicationProperties.setProperty(DEFAULT_COLOUR_PROT,
+    Cache.setPropertyNoSave(DEFAULT_COLOUR_PROT,
             protColour.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty(DEFAULT_COLOUR_NUC,
+    Cache.setPropertyNoSave(DEFAULT_COLOUR_NUC,
             nucColour.getSelectedItem().toString());
-    Cache.setColourProperty("ANNOTATIONCOLOUR_MIN",
+    Cache.setColourPropertyNoSave(ANNOTATIONCOLOUR_MIN,
             minColour.getBackground());
-    Cache.setColourProperty("ANNOTATIONCOLOUR_MAX",
+    Cache.setColourPropertyNoSave(ANNOTATIONCOLOUR_MAX,
             maxColour.getBackground());
 
     /*
      * Save Overview settings
      */
-    Cache.setColourProperty(GAP_COLOUR, gapColour.getBackground());
-    Cache.setColourProperty(HIDDEN_COLOUR, hiddenColour.getBackground());
-    Cache.applicationProperties.setProperty(USE_LEGACY_GAP,
+    Cache.setColourPropertyNoSave(GAP_COLOUR, gapColour.getBackground());
+    Cache.setColourPropertyNoSave(HIDDEN_COLOUR,
+            hiddenColour.getBackground());
+    Cache.setPropertyNoSave(USE_LEGACY_GAP,
             Boolean.toString(useLegacyGap.isSelected()));
-    Cache.applicationProperties.setProperty(SHOW_OV_HIDDEN_AT_START,
+    Cache.setPropertyNoSave(SHOW_OV_HIDDEN_AT_START,
             Boolean.toString(showHiddenAtStart.isSelected()));
 
     /*
      * Save Structure settings
      */
-    Cache.applicationProperties.setProperty(ADD_TEMPFACT_ANN,
+    Cache.setPropertyNoSave(ADD_TEMPFACT_ANN,
             Boolean.toString(addTempFactor.isSelected()));
-    Cache.applicationProperties.setProperty(ADD_SS_ANN,
+    Cache.setPropertyNoSave(ADD_SS_ANN,
             Boolean.toString(addSecondaryStructure.isSelected()));
-    Cache.applicationProperties.setProperty(USE_RNAVIEW,
+    Cache.setPropertyNoSave(USE_RNAVIEW,
             Boolean.toString(useRnaView.isSelected()));
-    Cache.applicationProperties.setProperty(STRUCT_FROM_PDB,
+    Cache.setPropertyNoSave(STRUCT_FROM_PDB,
             Boolean.toString(structFromPdb.isSelected()));
-    Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY,
+    Cache.setPropertyNoSave(STRUCTURE_DISPLAY,
             structViewer.getSelectedItem().toString());
+    Cache.setPropertyNoSave(STRUCTURE_DIMENSIONS,
+            structureDimensions.getText()); // BH 2019.07.12
     Cache.setOrRemove(CHIMERA_PATH, chimeraPath.getText());
-    Cache.applicationProperties.setProperty("MAP_WITH_SIFTS",
+    Cache.setPropertyNoSave(MAP_WITH_SIFTS,
             Boolean.toString(siftsMapping.isSelected()));
     SiftsSettings.setMapWithSifts(siftsMapping.isSelected());
 
     /*
      * Save Output settings
      */
-    Cache.applicationProperties.setProperty("EPS_RENDERING",
+    Cache.setPropertyNoSave("EPS_RENDERING",
             ((OptionsParam) epsRendering.getSelectedItem()).getCode());
-    Cache.applicationProperties.setProperty("HTML_RENDERING",
+    Cache.setPropertyNoSave("HTML_RENDERING",
             ((OptionsParam) htmlRendering.getSelectedItem()).getCode());
-    Cache.applicationProperties.setProperty("SVG_RENDERING",
+    Cache.setPropertyNoSave("SVG_RENDERING",
             ((OptionsParam) svgRendering.getSelectedItem()).getCode());
 
-    /*
-     * Save Connections settings
+    if (!Platform.isJS())
+    /**
+     * @j2sNative
      */
-    Cache.setOrRemove("DEFAULT_BROWSER", defaultBrowser.getText());
-
-    jalview.util.BrowserLauncher.resetBrowser();
+    {
+      // Java only
+      // Save Connections settings
+      Cache.setOrRemove("DEFAULT_BROWSER", defaultBrowser.getText());
+      BrowserLauncher.resetBrowser();
+    }
 
     // save user-defined and selected links
     String menuLinks = sequenceUrlLinks.writeUrlsAsString(true);
     if (menuLinks.isEmpty())
     {
-      Cache.applicationProperties.remove("SEQUENCE_LINKS");
+      Cache.removePropertyNoSave("SEQUENCE_LINKS");
     }
     else
     {
-      Cache.applicationProperties.setProperty("SEQUENCE_LINKS",
-              menuLinks.toString());
+      Cache.setPropertyNoSave("SEQUENCE_LINKS", menuLinks.toString());
     }
 
     String nonMenuLinks = sequenceUrlLinks.writeUrlsAsString(false);
     if (nonMenuLinks.isEmpty())
     {
-      Cache.applicationProperties.remove("STORED_LINKS");
+      Cache.removePropertyNoSave("STORED_LINKS");
     }
     else
     {
-      Cache.applicationProperties.setProperty("STORED_LINKS",
-              nonMenuLinks.toString());
+      Cache.setPropertyNoSave("STORED_LINKS", nonMenuLinks.toString());
     }
 
-    Cache.applicationProperties.setProperty("DEFAULT_URL",
+    Cache.setPropertyNoSave("DEFAULT_URL",
             sequenceUrlLinks.getPrimaryUrlId());
 
-    Cache.applicationProperties.setProperty("USE_PROXY",
+    Cache.setPropertyNoSave("USE_PROXY",
             Boolean.toString(useProxy.isSelected()));
 
     Cache.setOrRemove("PROXY_SERVER", proxyServerTB.getText());
@@ -784,52 +912,51 @@ public class Preferences extends GPreferences
     }
     if (!questionnaire.isSelected())
     {
-      Cache.setProperty("NOQUESTIONNAIRES", "true");
+      Cache.setProperty(NOQUESTIONNAIRES, "true");
     }
     else
     {
       // special - made easy to edit a property file to disable questionnaires
       // by just adding the given line
-      Cache.removeProperty("NOQUESTIONNAIRES");
+      Cache.removeProperty(NOQUESTIONNAIRES);
     }
 
     /*
      * Save Output settings
      */
-    Cache.applicationProperties.setProperty("BLC_JVSUFFIX",
+    Cache.setPropertyNoSave("BLC_JVSUFFIX",
             Boolean.toString(blcjv.isSelected()));
-    Cache.applicationProperties.setProperty("CLUSTAL_JVSUFFIX",
+    Cache.setPropertyNoSave("CLUSTAL_JVSUFFIX",
             Boolean.toString(clustaljv.isSelected()));
-    Cache.applicationProperties.setProperty("FASTA_JVSUFFIX",
+    Cache.setPropertyNoSave("FASTA_JVSUFFIX",
             Boolean.toString(fastajv.isSelected()));
-    Cache.applicationProperties.setProperty("MSF_JVSUFFIX",
+    Cache.setPropertyNoSave("MSF_JVSUFFIX",
             Boolean.toString(msfjv.isSelected()));
-    Cache.applicationProperties.setProperty("PFAM_JVSUFFIX",
+    Cache.setPropertyNoSave("PFAM_JVSUFFIX",
             Boolean.toString(pfamjv.isSelected()));
-    Cache.applicationProperties.setProperty("PILEUP_JVSUFFIX",
+    Cache.setPropertyNoSave("PILEUP_JVSUFFIX",
             Boolean.toString(pileupjv.isSelected()));
-    Cache.applicationProperties.setProperty("PIR_JVSUFFIX",
+    Cache.setPropertyNoSave("PIR_JVSUFFIX",
             Boolean.toString(pirjv.isSelected()));
-    Cache.applicationProperties.setProperty("PIR_MODELLER",
+    Cache.setPropertyNoSave("PIR_MODELLER",
             Boolean.toString(modellerOutput.isSelected()));
-    Cache.applicationProperties.setProperty("EXPORT_EMBBED_BIOJSON",
+    Cache.setPropertyNoSave("EXPORT_EMBBED_BIOJSON",
             Boolean.toString(embbedBioJSON.isSelected()));
     jalview.io.PIRFile.useModellerOutput = modellerOutput.isSelected();
 
-    Cache.applicationProperties.setProperty("FIGURE_AUTOIDWIDTH",
+    Cache.setPropertyNoSave("FIGURE_AUTOIDWIDTH",
             Boolean.toString(autoIdWidth.isSelected()));
     userIdWidth_actionPerformed();
-    Cache.applicationProperties.setProperty("FIGURE_FIXEDIDWIDTH",
-            userIdWidth.getText());
+    Cache.setPropertyNoSave("FIGURE_FIXEDIDWIDTH", userIdWidth.getText());
 
     /*
      * Save Editing settings
      */
-    Cache.applicationProperties.setProperty("AUTO_CALC_CONSENSUS",
+    Cache.setPropertyNoSave(AUTO_CALC_CONSENSUS,
             Boolean.toString(autoCalculateConsCheck.isSelected()));
-    Cache.applicationProperties.setProperty("SORT_BY_TREE",
+    Cache.setPropertyNoSave(SORT_BY_TREE,
             Boolean.toString(sortByTree.isSelected()));
-    Cache.applicationProperties.setProperty("PAD_GAPS",
+    Cache.setPropertyNoSave(PAD_GAPS,
             Boolean.toString(padGaps.isSelected()));
 
     if (!Platform.isJS())
@@ -837,31 +964,31 @@ public class Preferences extends GPreferences
       wsPrefs.updateAndRefreshWsMenuConfig(false);
     }
 
-    /*
-     * Save Backups settings
-     */
-    Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
-            Boolean.toString(enableBackupFiles.isSelected()));
+      /*
+       * Save Backups settings
+       */
+    Cache.setPropertyNoSave(BackupFiles.ENABLED,
+              Boolean.toString(enableBackupFiles.isSelected()));
     int preset = getComboIntStringKey(backupfilesPresetsCombo);
-    Cache.applicationProperties.setProperty(BackupFiles.NS + "_PRESET", Integer.toString(preset));
+    Cache.setPropertyNoSave(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.setPropertyNoSave(BackupFilesPresetEntry.CUSTOMCONFIG,
+              customBFPE.toString());
     }
 
     BackupFilesPresetEntry savedBFPE = BackupFilesPresetEntry.backupfilesPresetEntriesValues
             .get(preset);
-    Cache.applicationProperties.setProperty(
-            BackupFilesPresetEntry.SAVEDCONFIG, savedBFPE.toString());
+    Cache.setPropertyNoSave(BackupFilesPresetEntry.SAVEDCONFIG,
+            savedBFPE.toString());
 
     Cache.saveProperties();
-    Desktop.instance.doConfigureStructurePrefs();
+    Desktop.getInstance().doConfigureStructurePrefs();
     try
     {
       frame.setClosed(true);
@@ -870,6 +997,43 @@ public class Preferences extends GPreferences
     }
   }
 
+  public static void setAppletDefaults()
+  {
+
+    // http://www.jalview.org/old/v2_8/examples/appletParameters.html
+
+    // showConservation true or false Default is true.
+    // showQuality true or false Default is true.
+    // showConsensus true or false Default is true.
+    // showFeatureSettings true or false Shows the feature settings window when
+    // startin
+    // showTreeBootstraps true or false (default is true) show or hide branch
+    // bootstraps
+    // showTreeDistances true or false (default is true) show or hide branch
+    // lengths
+    // showUnlinkedTreeNodes true or false (default is false) indicate if
+    // unassociated nodes should be highlighted in the tree view
+    // showUnconserved true of false (default is false) When true, only gaps and
+    // symbols different to the consensus sequence ions of the alignment
+    // showGroupConsensus true of false (default is false) When true, shows
+    // consensus annotation row for any groups on the alignment. (since 2.7)
+    // showGroupConservation true of false (default is false) When true, shows
+    // amino-acid property conservation annotation row for any groups on the
+    // showConsensusHistogram true of false (default is true) When true, shows
+    // the percentage occurence of the consensus symbol for each column as a
+    // showSequenceLogo true of false (default is false) When true, shows a
+    // sequence logo above the consensus sequence (overlaid above the Consensus
+
+    Cache.setPropertyNoSave(SHOW_CONSERVATION, "true");
+    Cache.setPropertyNoSave(SHOW_QUALITY, "false");
+    Cache.setPropertyNoSave(SHOW_CONSENSUS, "true");
+    Cache.setPropertyNoSave(SHOW_UNCONSERVED, "false");
+    Cache.setPropertyNoSave(SHOW_GROUP_CONSERVATION, "false");
+    Cache.setPropertyNoSave(SHOW_GROUP_CONSENSUS, "false");
+
+    // TODO -- just a start here
+  }
+
   /**
    * Do any necessary validation before saving settings. Return focus to the
    * first tab which fails validation.
@@ -894,7 +1058,8 @@ public class Preferences extends GPreferences
   }
 
   /**
-   * DOCUMENT ME!
+   * Opens a file browser, and if a file is chosen, sets its path as the text of
+   * the 'startup file' text field
    */
   @Override
   public void startupFileTextfield_mouseClicked()
@@ -914,8 +1079,10 @@ public class Preferences extends GPreferences
       FileFormatI format = chooser.getSelectedFormat();
       if (format != null)
       {
-        Cache.applicationProperties.setProperty("DEFAULT_FILE_FORMAT",
-                format.getName());
+        /*
+         * saving properties to file is deferred to the 'OK' action
+         */
+        Cache.setPropertyNoSave("DEFAULT_FILE_FORMAT", format.getName());
       }
       startupFileTextfield
               .setText(chooser.getSelectedFile().getAbsolutePath());
@@ -972,7 +1139,7 @@ public class Preferences extends GPreferences
     boolean valid = false;
     while (!valid)
     {
-      if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
+      if (JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(), link,
               MessageManager.getString("label.new_sequence_url_link"),
               JvOptionPane.OK_CANCEL_OPTION, -1,
               null) == JvOptionPane.OK_OPTION)
@@ -1024,7 +1191,7 @@ public class Preferences extends GPreferences
     boolean valid = false;
     while (!valid)
     {
-      if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
+      if (JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(), link,
               MessageManager.getString("label.edit_sequence_url_link"),
               JvOptionPane.OK_CANCEL_OPTION, -1,
               null) == JvOptionPane.OK_OPTION)
@@ -1111,6 +1278,11 @@ public class Preferences extends GPreferences
     super.showunconserved_actionPerformed(e);
   }
 
+  /**
+   * not implemented -- returns empty list
+   * 
+   * @return
+   */
   public static List<String> getGroupURLLinks()
   {
     return groupURLLinks;
@@ -1197,7 +1369,7 @@ public class Preferences extends GPreferences
     } catch (NumberFormatException x)
     {
       userIdWidth.setText("");
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("warn.user_defined_width_requirements"),
               MessageManager.getString("label.invalid_id_column_width"),
@@ -1216,14 +1388,14 @@ public class Preferences extends GPreferences
    * Returns true if chimera path is to a valid executable, else show an error
    * dialog.
    */
-  private boolean validateChimeraPath()
+  protected boolean validateChimeraPath()
   {
     if (chimeraPath.getText().trim().length() > 0)
     {
       File f = new File(chimeraPath.getText());
       if (!f.canExecute())
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.invalid_chimera_path"),
                 MessageManager.getString("label.invalid_name"),
                 JvOptionPane.ERROR_MESSAGE);
@@ -1262,7 +1434,7 @@ public class Preferences extends GPreferences
     if (!found)
     {
       String[] options = { "OK", "Help" };
-      int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.desktop,
+      int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.getDesktopPane(),
               JvSwingUtils.wrapTooltip(true,
                       MessageManager.getString("label.chimera_missing")),
               "", JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
@@ -1335,7 +1507,7 @@ public class Preferences extends GPreferences
     }
   }
 
-  private class UrlListSelectionHandler implements ListSelectionListener
+  protected class UrlListSelectionHandler implements ListSelectionListener
   {
 
     @Override
@@ -1343,36 +1515,40 @@ public class Preferences extends GPreferences
     {
       ListSelectionModel lsm = (ListSelectionModel) e.getSource();
 
-      int index = lsm.getMinSelectionIndex();
-      if (index == -1)
-      {
-        // no selection, so disable delete/edit buttons
-        editLink.setEnabled(false);
-        deleteLink.setEnabled(false);
-        return;
-      }
-      int modelIndex = linkUrlTable.convertRowIndexToModel(index);
+      updateValueChanged(lsm.getMinSelectionIndex());
+    }
+  }
 
-      // enable/disable edit and delete link buttons
-      if (((UrlLinkTableModel) linkUrlTable.getModel())
-              .isRowDeletable(modelIndex))
-      {
-        deleteLink.setEnabled(true);
-      }
-      else
-      {
-        deleteLink.setEnabled(false);
-      }
+  public void updateValueChanged(int index)
+  {
+    if (index == -1)
+    {
+      // no selection, so disable delete/edit buttons
+      editLink.setEnabled(false);
+      deleteLink.setEnabled(false);
+      return;
+    }
+    int modelIndex = linkUrlTable.convertRowIndexToModel(index);
 
-      if (((UrlLinkTableModel) linkUrlTable.getModel())
-              .isRowEditable(modelIndex))
-      {
-        editLink.setEnabled(true);
-      }
-      else
-      {
-        editLink.setEnabled(false);
-      }
+    // enable/disable edit and delete link buttons
+    if (((UrlLinkTableModel) linkUrlTable.getModel())
+            .isRowDeletable(modelIndex))
+    {
+      deleteLink.setEnabled(true);
+    }
+    else
+    {
+      deleteLink.setEnabled(false);
+    }
+
+    if (((UrlLinkTableModel) linkUrlTable.getModel())
+            .isRowEditable(modelIndex))
+    {
+      editLink.setEnabled(true);
+    }
+    else
+    {
+      editLink.setEnabled(false);
     }
   }
 }
index 06c2fc9..1abe4d7 100644 (file)
@@ -518,7 +518,7 @@ public class BackupFiles
           MessageManager.getString("label.delete"),
           MessageManager.getString("label.rename") };
 
-      confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop,
+      confirmButton = JvOptionPane.showOptionDialog(Desktop.getDesktop(),
               messageSB.toString(),
               MessageManager.getString("label.backupfiles_confirm_delete"),
               JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
@@ -538,7 +538,7 @@ public class BackupFiles
           MessageManager.getString("label.delete"),
           MessageManager.getString("label.keep") };
 
-      confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop,
+      confirmButton = JvOptionPane.showOptionDialog(Desktop.getDesktop(),
               messageSB.toString(),
               MessageManager.getString("label.backupfiles_confirm_delete"),
               JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
@@ -573,7 +573,7 @@ public class BackupFiles
                       Long.toString(df.length()) }));
         }
 
-        int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop,
+        int confirmButton = JvOptionPane.showConfirmDialog(Desktop.getDesktop(),
                 messageSB.toString(),
                 MessageManager
                         .getString("label.backupfiles_confirm_delete"),
@@ -662,7 +662,7 @@ public class BackupFiles
                 "label.backupfiles_confirm_save_new_saved_file_not_ok"));
       }
 
-      int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop,
+      int confirmButton = JvOptionPane.showConfirmDialog(Desktop.getDesktop(),
               messageSB.toString(),
               MessageManager
                       .getString("label.backupfiles_confirm_save_file"),
index 6de3888..a59d62f 100755 (executable)
@@ -180,6 +180,8 @@ public class GPreferences extends JPanel
 
   protected JComboBox<String> structViewer = new JComboBox<>();
 
+  protected JTextField structureDimensions = new JTextField();
+
   protected JTextField chimeraPath = new JTextField();
 
   protected ButtonGroup mappingMethod = new ButtonGroup();
@@ -1289,6 +1291,7 @@ public class GPreferences extends JPanel
     structViewer.addItem(ViewerType.JMOL.name());
     structViewer.addItem(ViewerType.CHIMERA.name());
     structViewer.addActionListener(new ActionListener()
+
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1299,6 +1302,20 @@ public class GPreferences extends JPanel
     });
     structureTab.add(structViewer);
 
+    // BH 2019.07.12
+    ypos += lineSpacing;
+    JLabel dimLabel = new JLabel();
+    dimLabel.setFont(new java.awt.Font("SansSerif", 0, 11));
+    dimLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    dimLabel.setText(
+            MessageManager.getString("label.structure_dimensions"));
+    dimLabel.setBounds(new Rectangle(10, ypos, 140, height));
+    structureTab.add(dimLabel);
+
+    structureDimensions.setFont(LABEL_FONT);
+    structureDimensions.setBounds(new Rectangle(160, ypos, 120, height));
+    structureTab.add(structureDimensions);
+
     ypos += lineSpacing;
     JLabel pathLabel = new JLabel();
     pathLabel.setFont(new java.awt.Font("SansSerif", 0, 11));
@@ -1603,11 +1620,17 @@ public class GPreferences extends JPanel
     fontLabel.setHorizontalAlignment(SwingConstants.RIGHT);
     fontLabel.setText(MessageManager.getString("label.font"));
     fontSizeCB.setFont(LABEL_FONT);
-    fontSizeCB.setBounds(new Rectangle(320, 112, 65, 23));
+    fontSizeCB.setBounds(new Rectangle(320, 115, 65, 23)); // BH 2019.09.24 y
+                                                           // added 3 pixels for
+                                                           // Java/Windows
     fontStyleCB.setFont(LABEL_FONT);
-    fontStyleCB.setBounds(new Rectangle(382, 112, 80, 23));
+    fontStyleCB.setBounds(new Rectangle(382, 115, 80, 23)); // BH 2019.09.24 y
+                                                            // added 3 pixels
+                                                            // for Java/Windows
     fontNameCB.setFont(LABEL_FONT);
-    fontNameCB.setBounds(new Rectangle(172, 112, 147, 23));
+    fontNameCB.setBounds(new Rectangle(172, 115, 147, 23)); // BH 2019.09.24 y
+                                                            // added 3 pixels
+                                                            // for Java/Windows
     gapSymbolCB.setFont(LABEL_FONT);
     gapSymbolCB.setBounds(new Rectangle(172, 215, 69, 23));
     DefaultListCellRenderer dlcr = new DefaultListCellRenderer();
@@ -1652,7 +1675,18 @@ public class GPreferences extends JPanel
     sortAutocalc.setBounds(new Rectangle(290, 285, 165, 21));
 
     JPanel annsettingsPanel = new JPanel();
-    annsettingsPanel.setBounds(new Rectangle(173, 13, 320, 96));
+    annsettingsPanel.setBounds(new Rectangle(173, 13, 330, 100)); // BH
+                                                                 // 2019.09.24
+                                                                 // SwingJS
+                                                                 // needs a few
+                                                                  // more
+                                                                  // pixels.
+                                                                  // Java needs
+                                                                  // a bit more
+                                                                  // in height
+                                                                  // on Windows
+                                                                  // OS
+                                                                 // Was 320, 96
     annsettingsPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
     annsettingsPanel.setBorder(new EtchedBorder());
     visualTab.add(annsettingsPanel);
@@ -2056,12 +2090,12 @@ public class GPreferences extends JPanel
       backupsSetOptions(
               BackupFilesPresetEntry.backupfilesPresetEntriesValues
                       .get(key));
-    }
-    else
-    {
+      }
+      else
+      {
       Cache.log.error(
               "Preset '" + value + "' [key:" + key + "] not implemented");
-    }
+      }
 
     // Custom options will now be enabled when the customiseCheckbox is checked
     // (performed above)
@@ -2078,7 +2112,7 @@ public class GPreferences extends JPanel
       e = (IntKeyStringValueEntry) backupfilesPresetsCombo2
               .getSelectedItem();
     } catch (Exception ex)
-    {
+  {
       Cache.log.error(
               "Problem casting Combo entry to IntKeyStringValueEntry.");
       e = null;
@@ -2097,7 +2131,7 @@ public class GPreferences extends JPanel
       {
         e = (IntKeyStringValueEntry) backupfilesPresetsCombo2.getItemAt(i);
       } catch (Exception ex)
-      {
+    {
         Cache.log.error(
                 "Problem casting Combo entry to IntKeyStringValueEntry. Skipping item. ");
         continue;
@@ -2256,7 +2290,7 @@ public class GPreferences extends JPanel
     boolean ret = false;
     String warningMessage = MessageManager
             .getString("label.warning_confirm_change_reverse");
-    int confirm = JvOptionPane.showConfirmDialog(Desktop.desktop,
+    int confirm = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
             warningMessage,
             MessageManager.getString("label.change_increment_decrement"),
             JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE);