JAL-3631 Customise the user-space path in the install4j installation wizard. Not...
authorBen Soares <b.soares@dundee.ac.uk>
Fri, 19 Jul 2024 17:47:35 +0000 (18:47 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Fri, 19 Jul 2024 17:47:35 +0000 (18:47 +0100)
utils/install4j/install4j10_template.install4j

index 705a45c..564b1a5 100644 (file)
     <applications>
       <application id="installer" beanClass="com.install4j.runtime.beans.applications.InstallerApplication" actionElevationType="elevated" styleId="35" customIcnsFile="${compiler:JALVIEW_DIR}/${compiler:INSTALLER_MAC_ICON}" customIcoFile="${compiler:JALVIEW_DIR}/${compiler:INSTALLER_WINDOWS_ICON}">
         <serializedBean>
-          <property name="frameHeight" type="int" value="684" />
-          <property name="frameWidth" type="int" value="912" />
+          <property name="frameHeight" type="int" value="768" />
+          <property name="frameWidth" type="int" value="1024" />
           <property name="useCustomIcon" type="boolean" value="true" />
         </serializedBean>
         <styleOverrides>
@@ -678,11 +678,12 @@ return console.askOkCancel(message, true);
     )</visibilityScript>
               </formComponent>
               <formComponent id="2983" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" />
-              <formComponent name="Enable advanced options" id="3006" customizedId="US_ADVANCED_OPTIONS" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+              <formComponent name="Enable ADVANCED OPTIONS" id="3006" customizedId="US_ADVANCED_OPTIONS" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
                 <serializedBean>
-                  <property name="checkboxText" type="string">Enable advanced options</property>
+                  <property name="checkboxText" type="string">Enable advanced options for user installation</property>
                   <property name="coupledComponentIds">
                     <add type="string">2980</add>
+                    <add type="string">3014</add>
                     <add type="string">2975</add>
                     <add type="string">2989</add>
                   </property>
@@ -696,7 +697,7 @@ if (!JCheckBox.class.equals(cl_advancedOptions)) {
 JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
 boolean advancedOptions = fc_advancedOptions.isEnabled() &amp;&amp; jcb_advancedOptions.isSelected();
 
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("US_ALLOW_USER_APPDIR_UPDATES");
+FormComponent fc_userUpdates = formEnvironment.getFormComponentById("US_ALLOW_USER_APPDIR_UPDATES_ADVANCEDITEM");
 Class&lt;?&gt; cl_user = fc_userUpdates.getConfigurationObjectClass();
 if (!JCheckBox.class.equals(cl_user)) {
     return;
@@ -706,26 +707,31 @@ boolean userUpdates = fc_userUpdates.isEnabled() &amp;&amp; jcb_user.isSelected(
 
 boolean showWarning = advancedOptions &amp;&amp; (!userUpdates);
 
-formEnvironment.getFormComponentById("US_NO_UPDATES_WARNING").setVisible(showWarning);
+FormComponent fc_warning = formEnvironment.getFormComponentById("US_NO_UPDATES_WARNING");
+fc_warning.setVisible(showWarning);
 
-FormComponent fc_label = formEnvironment.getFormComponentById("US_LABEL");
-fc_label.setVisible(advancedOptions);
-fc_userUpdates.setVisible(advancedOptions);
+for (FormComponent fc: formEnvironment.getFormComponents()) {
+  if (formEnvironment.getId(fc).contains("ADVANCEDITEM")) {
+    fc.setVisible(advancedOptions);
+  }
+}
 </property>
                     </object>
                   </property>
                   <property name="variableName" type="string">advancedOptions</property>
                 </serializedBean>
               </formComponent>
-              <formComponent name="Strongly recommended text" id="2980" customizedId="US_LABEL" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetLeft="16">
+              <formComponent name="Strongly recommended text" id="2980" customizedId="US_LABEL_ADVANCEDITEM" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetLeft="16">
                 <serializedBean>
-                  <property name="labelHtml" type="string">&lt;html&gt;The following option is strongly recommended to be left as default unless there is a particular reason to change them.&lt;/html&gt;</property>
+                  <property name="labelHtml" type="string">&lt;html&gt;The following option is &lt;strong&gt;strongly recommended&lt;/strong&gt;
+to be left as default unless there is a particular
+reason to change it.&lt;/html&gt;</property>
                 </serializedBean>
                 <visibilityScript>context.getBooleanVariable("advancedOptions")
 </visibilityScript>
               </formComponent>
-              <formComponent id="3014" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" insetLeft="16" />
-              <formComponent name="BOTHSPACE: Allow automatic updates in user's home directory" id="2975" customizedId="US_ALLOW_USER_APPDIR_UPDATES" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
+              <formComponent id="3014" customizedId="US_VSPACE_ADVANCEDITEM" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" insetLeft="16" />
+              <formComponent name="BOTHSPACE: Allow automatic updates in user's home directory" id="2975" customizedId="US_ALLOW_USER_APPDIR_UPDATES_ADVANCEDITEM" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
                 <serializedBean>
                   <property name="checkboxText" type="string">Allow user-space updates for ${compiler:JALVIEW_APPLICATION_NAME} components</property>
                   <property name="helpText" type="string">&lt;html&gt;This option allows updates to ${compiler:JALVIEW_APPLICATION_NAME}
@@ -754,7 +760,7 @@ if (!JCheckBox.class.equals(cl_advancedOptions)) {
 JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
 boolean advancedOptions = fc_advancedOptions.isEnabled() &amp;&amp; jcb_advancedOptions.isSelected();
 
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("US_ALLOW_USER_APPDIR_UPDATES");
+FormComponent fc_userUpdates = formEnvironment.getFormComponentById("US_ALLOW_USER_APPDIR_UPDATES_ADVANCEDITEM");
 Class&lt;?&gt; cl_user = fc_userUpdates.getConfigurationObjectClass();
 if (!JCheckBox.class.equals(cl_user)) {
     return;
@@ -829,65 +835,88 @@ formEnvironment.getFormComponentById("US_NO_UPDATES_WARNING").setVisible(showWar
 </visibilityScript>
               </formComponent>
               <formComponent id="2984" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" />
-              <formComponent name="Enable advanced options" id="3007" customizedId="SS_ADVANCED_OPTIONS" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+              <formComponent name="Enable ADVANCED OPTIONS" id="3007" customizedId="SS_ADVANCED_OPTIONS" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
                 <serializedBean>
-                  <property name="checkboxText" type="string">Enable advanced options</property>
+                  <property name="checkboxText" type="string">Enable advanced options for system installation</property>
                   <property name="coupledComponentIds">
                     <add type="string">2985</add>
+                    <add type="string">3013</add>
+                    <add type="string">2986</add>
+                    <add type="string">2974</add>
+                    <add type="string">2988</add>
                   </property>
                   <property name="selectionScript">
                     <object class="com.install4j.api.beans.ScriptProperty">
                       <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-Class&lt;?&gt; cl_advancedOptions = fc_advancedOptions.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_advancedOptions)) {
-    return;
-}
+FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES_ADVANCEDITEM");
+FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES_ADVANCEDITEM");
+FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
+FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
+LayoutGroup lg = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
+
+// get boolean status of "Enable advanced options" checkbox
 JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = fc_advancedOptions.isEnabled() &amp;&amp; jcb_advancedOptions.isSelected();
+boolean advancedOptions = jcb_advancedOptions.isSelected();
 
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
-Class&lt;?&gt; cl_user = fc_userUpdates.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_user)) {
-    return;
-}
+// get boolean status of "Allow user-space updates" checkbox
 JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = fc_userUpdates.isEnabled() &amp;&amp; jcb_user.isSelected();
+boolean userUpdates = jcb_user.isSelected();
 
-FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
-Class&lt;?&gt; cl_installer = fc_installerUpdates.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_installer)) {
-    return;
-}
+// get boolean status of "Allow installation updates" checkbox
 JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-boolean installerUpdates = fc_installerUpdates.isEnabled() &amp;&amp; jcb_installer.isSelected();
+boolean installerUpdates = jcb_installer.isSelected();
 
-boolean showWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
+// get boolean status of "Customise the user-space path" checkbox
+JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
+boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
+
+// get String value of userAppdirPath text field
+JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
+String userAppdirPath = jtf_userAppdirPath.getText();
+
+// set visibility of all "ADVANCEDITEM" components (does not include groups)
+for (FormComponent fc: formEnvironment.getFormComponents()) {
+  String id = formEnvironment.getId(fc);
+  if (id.contains("ADVANCEDITEM")) {
+    fc.setVisible(advancedOptions);
+  }
+}
+// set visibility of customised user appdir path group (checkbox and text field)
+lg.setVisible(advancedOptions);
+
+// set enabled of userAppdirPath text field
+fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath);
 
-formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING").setVisible(showWarning);
+// set enabled of customised user appdir path group
+lg.setEnabled(advancedOptions &amp;&amp; userUpdates);
 
+// set enabled of allow installation updates checkbox
 fc_installerUpdates.setEnabled(!userUpdates);
 
-FormComponent fc_label = formEnvironment.getFormComponentById("SS_LABEL");
-fc_label.setVisible(advancedOptions);
-fc_userUpdates.setVisible(advancedOptions);
-fc_installerUpdates.setVisible(advancedOptions);
+// should we show the No updates warning?
+boolean showWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
+FormComponent fc_warning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
+fc_warning.setVisible(showWarning);
 </property>
                     </object>
                   </property>
                   <property name="variableName" type="string">advancedOptions</property>
                 </serializedBean>
               </formComponent>
-              <formComponent name="Strongly recommended text" id="2985" customizedId="SS_LABEL" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetLeft="16">
+              <formComponent name="Strongly recommended text" id="2985" customizedId="SS_LABEL_ADVANCEDITEM" beanClass="com.install4j.runtime.beans.formcomponents.MultilineHtmlLabelComponent" insetLeft="16">
                 <serializedBean>
-                  <property name="labelHtml" type="string">&lt;html&gt;The following options are strongly recommended to be left as default unless there is a particular reason to change them.&lt;/html&gt;</property>
+                  <property name="labelHtml" type="string">&lt;html&gt;The following options are &lt;strong&gt;strongly recommended&lt;/strong&gt; to be left as default unless there is a particular reason to change them.&lt;/html&gt;</property>
                 </serializedBean>
                 <visibilityScript>context.getBooleanVariable("advancedOptions")
 </visibilityScript>
               </formComponent>
-              <formComponent id="3013" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" insetLeft="16" />
-              <formComponent name="BOTHSPACE: Allow automatic updates in user's home directory" id="2986" customizedId="SS_ALLOW_USER_APPDIR_UPDATES" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
+              <formComponent id="3013" customizedId="SS_VSPACE_ADVANCEDITEM" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" insetLeft="16" />
+              <formComponent name="BOTHSPACE: Allow automatic updates in user's home directory" id="2986" customizedId="SS_ALLOW_USER_APPDIR_UPDATES_ADVANCEDITEM" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
                 <serializedBean>
                   <property name="checkboxText" type="string">Allow user-space updates for ${compiler:JALVIEW_APPLICATION_NAME} components</property>
+                  <property name="coupledComponentIds">
+                    <add type="string">3021</add>
+                  </property>
                   <property name="helpText" type="string">&lt;html&gt;This option allows updates to ${compiler:JALVIEW_APPLICATION_NAME}
 &lt;br&gt;
 components to be automatically downloaded
@@ -900,42 +929,62 @@ the installation location.
 &lt;br&gt;
 &lt;br&gt;
 On ${installer:osName}, user updates will be installed under
-&lt;br&gt;
 &lt;pre&gt;${installer:userDefaultAppdirBase}&lt;/pre&gt;
+unless customised below.
 &lt;/html&gt;</property>
                   <property name="initiallySelected" type="boolean" value="true" />
-                  <property name="inverseCoupling" type="boolean" value="true" />
                   <property name="selectionScript">
                     <object class="com.install4j.api.beans.ScriptProperty">
                       <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-Class&lt;?&gt; cl_advancedOptions = fc_advancedOptions.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_advancedOptions)) {
-    return;
-}
+FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES_ADVANCEDITEM");
+FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES_ADVANCEDITEM");
+FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
+FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
+LayoutGroup lg = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
+
+// get boolean status of "Enable advanced options" checkbox
 JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = fc_advancedOptions.isEnabled() &amp;&amp; jcb_advancedOptions.isSelected();
+boolean advancedOptions = jcb_advancedOptions.isSelected();
 
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
-Class&lt;?&gt; cl_user = fc_userUpdates.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_user)) {
-    return;
-}
+// get boolean status of "Allow user-space updates" checkbox
 JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = fc_userUpdates.isEnabled() &amp;&amp; jcb_user.isSelected();
+boolean userUpdates = jcb_user.isSelected();
 
-FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
-Class&lt;?&gt; cl_installer = fc_installerUpdates.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_installer)) {
-    return;
-}
+// get boolean status of "Allow installation updates" checkbox
 JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-boolean installerUpdates = fc_installerUpdates.isEnabled() &amp;&amp; jcb_installer.isSelected();
+boolean installerUpdates = jcb_installer.isSelected();
 
-boolean showWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
+// get boolean status of "Customise the user-space path" checkbox
+JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
+boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
+
+// get String value of userAppdirPath text field
+JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
+String userAppdirPath = jtf_userAppdirPath.getText();
 
-formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING").setVisible(showWarning);
+// set visibility of all "ADVANCEDITEM" components (does not include groups)
+for (FormComponent fc: formEnvironment.getFormComponents()) {
+  String id = formEnvironment.getId(fc);
+  if (id.contains("ADVANCEDITEM")) {
+    fc.setVisible(advancedOptions);
+  }
+}
+// set visibility of customised user appdir path group (checkbox and text field)
+lg.setVisible(advancedOptions);
+
+// set enabled of userAppdirPath text field
+fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath);
+
+// set enabled of customised user appdir path group
+lg.setEnabled(advancedOptions &amp;&amp; userUpdates);
 
+// set enabled of allow installation updates checkbox
 fc_installerUpdates.setEnabled(!userUpdates);
+
+// should we show the No updates warning?
+boolean showWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
+FormComponent fc_warning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
+fc_warning.setVisible(showWarning);
 </property>
                     </object>
                   </property>
@@ -944,7 +993,125 @@ fc_installerUpdates.setEnabled(!userUpdates);
                 <visibilityScript>context.getBooleanVariable("advancedOptions")
 </visibilityScript>
               </formComponent>
-              <formComponent name="SYSTEMSPACE: Allow automatic updates in the installation directory" id="2974" customizedId="SS_ALLOW_INSTALLER_APPDIR_UPDATES" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
+              <group name="SYSTEMSPACE: Customise user appDir path" id="3021" customizedId="SS_SET_USER_APPDIR_PATH" beanClass="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup">
+                <serializedBean>
+                  <property name="insets">
+                    <object class="java.awt.Insets">
+                      <int>0</int>
+                      <int>32</int>
+                      <int>0</int>
+                      <int>0</int>
+                    </object>
+                  </property>
+                  <property name="visibilityScript">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">context.getBooleanVariable("advancedOptions")
+</property>
+                    </object>
+                  </property>
+                </serializedBean>
+                <beans>
+                  <formComponent name="SYSTEMSPACE: Allow setting userAppdirPath" id="3022" customizedId="SS_ALLOW_USER_APPDIR_PATH" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                    <serializedBean>
+                      <property name="checkboxText" type="string">Customise the user-space path</property>
+                      <property name="selectionScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
+FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES_ADVANCEDITEM");
+FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES_ADVANCEDITEM");
+FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
+FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
+LayoutGroup lg = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
+
+// get boolean status of "Enable advanced options" checkbox
+JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
+boolean advancedOptions = jcb_advancedOptions.isSelected();
+
+// get boolean status of "Allow user-space updates" checkbox
+JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
+boolean userUpdates = jcb_user.isSelected();
+
+// get boolean status of "Allow installation updates" checkbox
+JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
+boolean installerUpdates = jcb_installer.isSelected();
+
+// get boolean status of "Customise the user-space path" checkbox
+JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
+boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
+
+// get String value of userAppdirPath text field
+JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
+String userAppdirPath = jtf_userAppdirPath.getText();
+
+// set visibility of all "ADVANCEDITEM" components (does not include groups)
+for (FormComponent fc: formEnvironment.getFormComponents()) {
+  String id = formEnvironment.getId(fc);
+  if (id.contains("ADVANCEDITEM")) {
+    fc.setVisible(advancedOptions);
+  }
+}
+// set visibility of customised user appdir path group (checkbox and text field)
+lg.setVisible(advancedOptions);
+
+// set enabled of userAppdirPath text field
+fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath);
+
+// set enabled of customised user appdir path group
+lg.setEnabled(advancedOptions &amp;&amp; userUpdates);
+
+// set enabled of allow installation updates checkbox
+fc_installerUpdates.setEnabled(!userUpdates);
+
+// should we show the No updates warning?
+boolean showWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
+FormComponent fc_warning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
+fc_warning.setVisible(showWarning);
+</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">allowSetUserAppdirPath</property>
+                    </serializedBean>
+                  </formComponent>
+                  <formComponent name="SYSTEMSPACE: Set userAppdirPath" id="3024" customizedId="SS_USER_APPDIR_PATH" beanClass="com.install4j.runtime.beans.formcomponents.TextfieldComponent">
+                    <serializedBean>
+                      <property name="helpText" type="string">&lt;html&gt;The base path where individual users' updates
+&lt;br&gt;
+will be stored.
+&lt;br&gt;
+&lt;strong&gt;Only change this option if you need to!&lt;/strong&gt;
+&lt;br&gt;
+The following substitutions will be made:
+&lt;table&gt;
+&lt;tr&gt;&lt;td&gt;%u&lt;/td&gt;&lt;td&gt;The user's username&lt;/td&gt;&lt;/tr&gt;
+&lt;tr&gt;&lt;td&gt;%h&lt;/td&gt;&lt;td&gt;The user's home directory path&lt;/td&gt;&lt;/tr&gt;
+&lt;tr&gt;&lt;td&gt;~&lt;/td&gt;&lt;td&gt;(at start) The user's home directory path&lt;/td&gt;&lt;/tr&gt;
+&lt;/table&gt;
+At least one of the above substitutions should be used.
+&lt;br&gt;
+The default value on ${installer:osName} is
+&lt;br&gt;
+&lt;pre&gt;${installer:userDefaultAppdirBase}&lt;/pre&gt;
+&lt;/html&gt;</property>
+                      <property name="inputVerifier">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">boolean u = text.contains("%u");
+boolean h = text.contains("%h");
+boolean t = text.startsWith("~" + (String)context.getVariable("sys.fileSeparator"));
+
+boolean valid = text.length() == 0 || u || h || t;
+
+if (!valid) {
+    // popup warning?
+}
+return true;</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">userAppdirPath</property>
+                    </serializedBean>
+                  </formComponent>
+                </beans>
+              </group>
+              <formComponent name="SYSTEMSPACE: Allow automatic updates in the installation directory" id="2974" customizedId="SS_ALLOW_INSTALLER_APPDIR_UPDATES_ADVANCEDITEM" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" insetLeft="16">
                 <serializedBean>
                   <property name="checkboxText" type="string">Allow installation updates for ${compiler:JALVIEW_APPLICATION_NAME} components</property>
                   <property name="helpText" type="string">&lt;html&gt;This option allows updates to ${compiler:JALVIEW_APPLICATION_NAME}
@@ -973,32 +1140,56 @@ Installation updates will be installed into
                   <property name="selectionScript">
                     <object class="com.install4j.api.beans.ScriptProperty">
                       <property name="value" type="string">FormComponent fc_advancedOptions = formEnvironment.getFormComponentById("SS_ADVANCED_OPTIONS");
-Class&lt;?&gt; cl_advancedOptions = fc_advancedOptions.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_advancedOptions)) {
-    return;
-}
+FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES_ADVANCEDITEM");
+FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES_ADVANCEDITEM");
+FormComponent fc_allowUserAppdirPath = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_PATH");
+FormComponent fc_userAppdirPath = formEnvironment.getFormComponentById("SS_USER_APPDIR_PATH");
+LayoutGroup lg = formEnvironment.getLayoutGroupById("SS_SET_USER_APPDIR_PATH");
+
+// get boolean status of "Enable advanced options" checkbox
 JCheckBox jcb_advancedOptions = (JCheckBox) fc_advancedOptions.getConfigurationObject();
-boolean advancedOptions = fc_advancedOptions.isEnabled() &amp;&amp; jcb_advancedOptions.isSelected();
+boolean advancedOptions = jcb_advancedOptions.isSelected();
 
-FormComponent fc_userUpdates = formEnvironment.getFormComponentById("SS_ALLOW_USER_APPDIR_UPDATES");
-Class&lt;?&gt; cl_user = fc_userUpdates.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_user)) {
-    return;
-}
+// get boolean status of "Allow user-space updates" checkbox
 JCheckBox jcb_user = (JCheckBox) fc_userUpdates.getConfigurationObject();
-boolean userUpdates = fc_userUpdates.isEnabled() &amp;&amp; jcb_user.isSelected();
+boolean userUpdates = jcb_user.isSelected();
 
-FormComponent fc_installerUpdates = formEnvironment.getFormComponentById("SS_ALLOW_INSTALLER_APPDIR_UPDATES");
-Class&lt;?&gt; cl_installer = fc_installerUpdates.getConfigurationObjectClass();
-if (!JCheckBox.class.equals(cl_installer)) {
-    return;
-}
+// get boolean status of "Allow installation updates" checkbox
 JCheckBox jcb_installer = (JCheckBox) fc_installerUpdates.getConfigurationObject();
-boolean installerUpdates = fc_installerUpdates.isEnabled() &amp;&amp; jcb_installer.isSelected();
+boolean installerUpdates = jcb_installer.isSelected();
 
-boolean showWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
+// get boolean status of "Customise the user-space path" checkbox
+JCheckBox jcb_allowUserAppdirPath = (JCheckBox) fc_allowUserAppdirPath.getConfigurationObject();
+boolean allowUserAppdirPath = jcb_allowUserAppdirPath.isSelected();
 
-formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING").setVisible(showWarning);</property>
+// get String value of userAppdirPath text field
+JTextField jtf_userAppdirPath = (JTextField) fc_userAppdirPath.getConfigurationObject();
+String userAppdirPath = jtf_userAppdirPath.getText();
+
+// set visibility of all "ADVANCEDITEM" components (does not include groups)
+for (FormComponent fc: formEnvironment.getFormComponents()) {
+  String id = formEnvironment.getId(fc);
+  if (id.contains("ADVANCEDITEM")) {
+    fc.setVisible(advancedOptions);
+  }
+}
+// set visibility of customised user appdir path group (checkbox and text field)
+lg.setVisible(advancedOptions);
+
+// set enabled of userAppdirPath text field
+fc_userAppdirPath.setEnabled(advancedOptions &amp;&amp; allowUserAppdirPath);
+
+// set enabled of customised user appdir path group
+lg.setEnabled(advancedOptions &amp;&amp; userUpdates);
+
+// set enabled of allow installation updates checkbox
+fc_installerUpdates.setEnabled(!userUpdates);
+
+// should we show the No updates warning?
+boolean showWarning = advancedOptions &amp;&amp; !(userUpdates || installerUpdates);
+FormComponent fc_warning = formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING");
+fc_warning.setVisible(showWarning);
+</property>
                     </object>
                   </property>
                   <property name="variableName" type="string">allowInstallerAppdirUpdates</property>
@@ -1022,6 +1213,22 @@ formEnvironment.getFormComponentById("SS_NO_UPDATES_WARNING").setVisible(showWar
   || context.getBooleanVariable("allowInstallerAppdirUpdates")
 )</visibilityScript>
               </formComponent>
+              <formComponent name="SYSTEMSPACE: Invalid user-space path warning" id="3027" customizedId="SS_INVALID_USER_APPDIR_PATH_WARNING" beanClass="com.install4j.runtime.beans.formcomponents.LabelComponent" insetLeft="16">
+                <serializedBean>
+                  <property name="labelIconFile">
+                    <object class="com.install4j.api.beans.ExternalFile">
+                      <string>${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/warning.png</string>
+                    </object>
+                  </property>
+                  <property name="labelText" type="string">No automatic updates will occur when Jalview is launched</property>
+                </serializedBean>
+                <visibilityScript>context.getBooleanVariable("advancedOptions")
+&amp;&amp;
+!(
+  context.getBooleanVariable("allowUserDefaultAppdirUpdates")
+  || context.getBooleanVariable("allowInstallerAppdirUpdates")
+)</visibilityScript>
+              </formComponent>
             </formComponents>
           </screen>
           <screen id="15" beanClass="com.install4j.runtime.beans.screens.InstallationScreen" rollbackBarrier="true" rollbackBarrierExitCode="0">
@@ -1360,6 +1567,22 @@ return advanced ? !allowUser : false;</property>
                       <property name="variableName" type="string">disableUserDefaultAppdirUpdates</property>
                     </serializedBean>
                   </action>
+                  <action name="Set setUserAppdirPath" id="3025" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">boolean advanced = context.getBooleanVariable("advancedOptions");
+boolean allowUser = context.getBooleanVariable("allowUserDefaultAppdirUpdates");
+boolean allowSetUserAppdirPath = context.getBooleanVariable("allowSetUserAppdirPath");
+String userAppdirPath = (String)context.getVariable("userAppdirPath");
+boolean saneValue = userAppdirPath != null &amp;&amp; userAppdirPath.length() &gt; 0;
+
+return advanced &amp;&amp; allowUser &amp;&amp; allowSetUserAppdirPath &amp;&amp; saneValue ? userAppdirPath : null;</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">setUserAppdirPath</property>
+                    </serializedBean>
+                  </action>
                   <action name="Set disableUpdates" id="3012" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="script">
@@ -1392,6 +1615,17 @@ return advanced ? !( allowUser || allowInstaller ) : false;</property>
                     </serializedBean>
                     <condition>!context.getBooleanVariable("disableUserDefaultAppdirUpdates")</condition>
                   </action>
+                  <action name="Set setUserAppdirPath property" id="3026" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="launcherId" type="string">2823</property>
+                      <property name="vmOptions" type="array" elementType="string" length="3">
+                        <element index="0"># The following line sets a custom path for user-space updates -- use with caution.</element>
+                        <element index="1"># If unset, the default is ${installer:userDefaultAppdirBase} for ${installer:osName}</element>
+                        <element index="2">-Dsetuserappdirpath=${installer:setUserAppdirPath}</element>
+                      </property>
+                    </serializedBean>
+                    <condition>(String)context.getVariable("setUserAppdirPath") != null</condition>
+                  </action>
                   <action name="Disable user-space updates" id="2997" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="launcherId" type="string">2823</property>