JAL-3633 Added Preferences for Proxy Authentication. No jalview_properties yet
[jalview.git] / src / jalview / jbgui / GPreferences.java
index dcbe020..ca5b203 100755 (executable)
  */
 package jalview.jbgui;
 
-import jalview.bin.Cache;
-import jalview.fts.core.FTSDataColumnPreferences;
-import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
-import jalview.fts.service.pdb.PDBFTSRestClient;
-import jalview.gui.JalviewBooleanRadioButtons;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.io.BackupFilenameFilter;
-import jalview.io.BackupFiles;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -49,8 +38,8 @@ import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Arrays;
+import java.util.List;
 
 import javax.swing.AbstractCellEditor;
 import javax.swing.BorderFactory;
@@ -61,8 +50,8 @@ import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JFileChooser;
 import javax.swing.JLabel;
-import javax.swing.JList;
 import javax.swing.JPanel;
+import javax.swing.JPasswordField;
 import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
 import javax.swing.JSpinner;
@@ -83,6 +72,22 @@ import javax.swing.event.ChangeListener;
 import javax.swing.table.TableCellEditor;
 import javax.swing.table.TableCellRenderer;
 
+import jalview.bin.Cache;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.gui.Desktop;
+import jalview.gui.JalviewBooleanRadioButtons;
+import jalview.gui.JvOptionPane;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.BackupFilenameParts;
+import jalview.io.BackupFiles;
+import jalview.io.BackupFilesPresetEntry;
+import jalview.io.IntKeyStringValueEntry;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
 /**
  * Base class for the Preferences panel.
  * 
@@ -176,7 +181,9 @@ public class GPreferences extends JPanel
 
   protected JComboBox<String> structViewer = new JComboBox<>();
 
-  protected JTextField chimeraPath = new JTextField();
+  protected JLabel structureViewerPathLabel;
+
+  protected JTextField structureViewerPath = new JTextField();
 
   protected ButtonGroup mappingMethod = new ButtonGroup();
 
@@ -223,17 +230,45 @@ public class GPreferences extends JPanel
 
   protected JButton userOnly = new JButton();
 
+  protected JLabel httpLabel = new JLabel();
+
+  protected JLabel httpsLabel = new JLabel();
+
   protected JLabel portLabel = new JLabel();
 
   protected JLabel serverLabel = new JLabel();
 
-  protected JTextField proxyServerTB = new JTextField();
+  protected JLabel portLabel2 = new JLabel();
+
+  protected JLabel serverLabel2 = new JLabel();
 
-  protected JTextField proxyPortTB = new JTextField();
+  protected JLabel proxyAuthUsernameLabel = new JLabel();
+
+  protected JLabel proxyAuthPasswordLabel = new JLabel();
+
+  protected JTextField proxyServerHttpTB = new JTextField();
+
+  protected JTextField proxyPortHttpTB = new JTextField();
+
+  protected JTextField proxyServerHttpsTB = new JTextField();
+
+  protected JTextField proxyPortHttpsTB = new JTextField();
+
+  protected JCheckBox proxyAuth = new JCheckBox();
+
+  protected JTextField proxyAuthUsernameTB = new JTextField();
+
+  protected JTextField proxyAuthPasswordTB = new JPasswordField();
 
   protected JTextField defaultBrowser = new JTextField();
 
-  protected JCheckBox useProxy = new JCheckBox();
+  protected ButtonGroup proxyType = new ButtonGroup();
+
+  protected JRadioButton noProxy = new JRadioButton();
+
+  protected JRadioButton systemProxy = new JRadioButton();
+
+  protected JRadioButton customProxy = new JRadioButton();
 
   protected JCheckBox usagestats = new JCheckBox();
 
@@ -246,6 +281,10 @@ public class GPreferences extends JPanel
    */
   protected JComboBox<Object> epsRendering = new JComboBox<>();
 
+  protected JComboBox<Object> htmlRendering = new JComboBox<>();
+
+  protected JComboBox<Object> svgRendering = new JComboBox<>();
+
   protected JLabel userIdWidthlabel = new JLabel();
 
   protected JCheckBox autoIdWidth = new JCheckBox();
@@ -294,7 +333,15 @@ public class GPreferences extends JPanel
 
   protected JPanel presetsPanel = new JPanel();
 
-  protected JComboBox<IntKeyStringValueEntry> backupfilesPresetsCombo = new JComboBox<>();
+  protected JLabel presetsComboLabel = new JLabel();
+
+  protected JCheckBox customiseCheckbox = new JCheckBox();
+
+  protected JButton revertButton = new JButton();
+
+  protected JComboBox<Object> backupfilesPresetsCombo = new JComboBox<>();
+
+  private int backupfilesPresetsComboLastSelected = 0;
 
   protected JPanel suffixPanel = new JPanel();
 
@@ -364,8 +411,11 @@ public class GPreferences extends JPanel
     tabbedPane.add(initConnectionsTab(),
             MessageManager.getString("label.connections"));
 
-    tabbedPane.add(initBackupsTab(),
-            MessageManager.getString("label.backups"));
+       if (!Platform.isJS()) 
+       {
+         tabbedPane.add(initBackupsTab(), 
+                       MessageManager.getString("label.backups"));
+       }
 
     tabbedPane.add(initLinksTab(),
             MessageManager.getString("label.urllinks"));
@@ -379,8 +429,11 @@ public class GPreferences extends JPanel
     /*
      * See WsPreferences for the real work of configuring this tab.
      */
-    wsTab.setLayout(new BorderLayout());
-    tabbedPane.add(wsTab, MessageManager.getString("label.web_services"));
+    if (!Platform.isJS())
+    {
+      wsTab.setLayout(new BorderLayout());
+      tabbedPane.add(wsTab, MessageManager.getString("label.web_services"));
+    }
 
     /*
      * Handler to validate a tab before leaving it - currently only for
@@ -438,7 +491,7 @@ public class GPreferences extends JPanel
   }
 
   /**
-   * Initialises the Output tabbed panel.
+   * Initialises the Output tab
    * 
    * @return
    */
@@ -446,18 +499,35 @@ public class GPreferences extends JPanel
   {
     JPanel outputTab = new JPanel();
     outputTab.setLayout(null);
-    JLabel epsLabel = new JLabel();
+
+    JLabel epsLabel = new JLabel(
+            MessageManager.formatMessage("label.rendering_style", "EPS"));
     epsLabel.setFont(LABEL_FONT);
     epsLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-    epsLabel.setText(MessageManager.getString("label.eps_rendering_style"));
-    epsLabel.setBounds(new Rectangle(9, 31, 140, 24));
+    epsLabel.setBounds(new Rectangle(9, 31, 160, 24));
     epsRendering.setFont(LABEL_FONT);
-    epsRendering.setBounds(new Rectangle(154, 34, 187, 21));
+    epsRendering.setBounds(new Rectangle(174, 34, 187, 21));
+    JLabel htmlLabel = new JLabel(
+            MessageManager.formatMessage("label.rendering_style", "HTML"));
+    htmlLabel.setFont(LABEL_FONT);
+    htmlLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    htmlLabel.setBounds(new Rectangle(9, 55, 160, 24));
+    htmlRendering.setFont(LABEL_FONT);
+    htmlRendering.setBounds(new Rectangle(174, 58, 187, 21));
+    JLabel svgLabel = new JLabel(
+            MessageManager.formatMessage("label.rendering_style", "SVG"));
+    svgLabel.setFont(LABEL_FONT);
+    svgLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    svgLabel.setBounds(new Rectangle(9, 79, 160, 24));
+    svgRendering.setFont(LABEL_FONT);
+    svgRendering.setBounds(new Rectangle(174, 82, 187, 21));
+
     JLabel jLabel1 = new JLabel();
     jLabel1.setFont(LABEL_FONT);
     jLabel1.setHorizontalAlignment(SwingConstants.CENTER);
     jLabel1.setText(MessageManager.getString("label.append_start_end"));
     jLabel1.setFont(LABEL_FONT);
+
     fastajv.setFont(LABEL_FONT);
     fastajv.setHorizontalAlignment(SwingConstants.LEFT);
     clustaljv.setText(MessageManager.getString("label.clustal") + "     ");
@@ -474,7 +544,7 @@ public class GPreferences extends JPanel
     TitledBorder titledBorder2 = new TitledBorder(
             MessageManager.getString("label.file_output"));
     jPanel11.setBorder(titledBorder2);
-    jPanel11.setBounds(new Rectangle(30, 72, 196, 182));
+    jPanel11.setBounds(new Rectangle(30, 120, 196, 182));
     GridLayout gridLayout3 = new GridLayout();
     jPanel11.setLayout(gridLayout3);
     gridLayout3.setRows(8);
@@ -493,7 +563,7 @@ public class GPreferences extends JPanel
             MessageManager.getString("label.automatically_set_id_width"));
     autoIdWidth.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager
             .getString("label.adjusts_width_generated_eps_png")));
-    autoIdWidth.setBounds(new Rectangle(228, 96, 188, 23));
+    autoIdWidth.setBounds(new Rectangle(228, 144, 320, 23));
     autoIdWidth.addActionListener(new ActionListener()
     {
 
@@ -511,10 +581,10 @@ public class GPreferences extends JPanel
     userIdWidthlabel.setToolTipText(
             JvSwingUtils.wrapTooltip(true, MessageManager.getString(
                     "label.manually_specify_width_left_column")));
-    userIdWidthlabel.setBounds(new Rectangle(236, 120, 168, 23));
+    userIdWidthlabel.setBounds(new Rectangle(236, 168, 320, 23));
     userIdWidth.setFont(JvSwingUtils.getTextAreaFont());
     userIdWidth.setText("");
-    userIdWidth.setBounds(new Rectangle(232, 144, 84, 23));
+    userIdWidth.setBounds(new Rectangle(232, 192, 84, 23));
     userIdWidth.addActionListener(new ActionListener()
     {
 
@@ -527,13 +597,10 @@ public class GPreferences extends JPanel
     modellerOutput.setFont(LABEL_FONT);
     modellerOutput
             .setText(MessageManager.getString("label.use_modeller_output"));
-    modellerOutput.setBounds(new Rectangle(228, 226, 168, 23));
+    modellerOutput.setBounds(new Rectangle(228, 274, 320, 23));
     embbedBioJSON.setFont(LABEL_FONT);
     embbedBioJSON.setText(MessageManager.getString("label.embbed_biojson"));
-    embbedBioJSON.setBounds(new Rectangle(228, 200, 250, 23));
-
-    TitledBorder backupFilesBorder = new TitledBorder(
-            MessageManager.getString("label.backup_files"));
+    embbedBioJSON.setBounds(new Rectangle(228, 248, 250, 23));
 
     jPanel11.add(jLabel1);
     jPanel11.add(blcjv);
@@ -547,9 +614,19 @@ public class GPreferences extends JPanel
     outputTab.add(userIdWidth);
     outputTab.add(userIdWidthlabel);
     outputTab.add(modellerOutput);
-    outputTab.add(embbedBioJSON);
-    outputTab.add(epsLabel);
-    outputTab.add(epsRendering);
+    if (!Platform.isJS())
+    {
+      /*
+       * JalviewJS doesn't support Lineart option or SVG output
+       */
+      outputTab.add(embbedBioJSON);
+      outputTab.add(epsLabel);
+      outputTab.add(epsRendering);
+      outputTab.add(htmlLabel);
+      outputTab.add(htmlRendering);
+      outputTab.add(svgLabel);
+      outputTab.add(svgRendering);
+    }
     outputTab.add(jPanel11);
     return outputTab;
   }
@@ -621,6 +698,8 @@ public class GPreferences extends JPanel
                     GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
                     new Insets(0, 2, 5, 5), 70, 1));
 
+    versioncheck.setVisible(false);
+
     // Add padding so the panel doesn't look ridiculous
     JPanel spacePanel = new JPanel();
     connectTab.add(spacePanel,
@@ -642,6 +721,7 @@ public class GPreferences extends JPanel
     linkTab.setLayout(new GridBagLayout());
 
     // Set up table for Url links
+    linkUrlTable.getTableHeader().setReorderingAllowed(false);
     linkUrlTable.setFillsViewportHeight(true);
     linkUrlTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
     linkUrlTable.setAutoCreateRowSorter(true);
@@ -829,63 +909,223 @@ public class GPreferences extends JPanel
   private JPanel initConnTabProxyPanel()
   {
     // Label for server text box
-    serverLabel.setText(MessageManager.getString("label.address"));
+    serverLabel.setText(MessageManager.getString("label.host") + ": ");
     serverLabel.setHorizontalAlignment(SwingConstants.RIGHT);
     serverLabel.setFont(LABEL_FONT);
+    serverLabel2.setText(MessageManager.getString("label.host") + ": ");
+    serverLabel2.setHorizontalAlignment(SwingConstants.RIGHT);
+    serverLabel2.setFont(LABEL_FONT);
 
     // Proxy server and port text boxes
-    proxyServerTB.setFont(LABEL_FONT);
-    proxyPortTB.setFont(LABEL_FONT);
+    proxyServerHttpTB.setFont(LABEL_FONT);
+    proxyServerHttpTB.setColumns(40);
+    proxyPortHttpTB.setFont(LABEL_FONT);
+    proxyPortHttpTB.setColumns(4);
+    proxyServerHttpsTB.setFont(LABEL_FONT);
+    proxyServerHttpsTB.setColumns(40);
+    proxyPortHttpsTB.setFont(LABEL_FONT);
+    proxyPortHttpsTB.setColumns(4);
+    proxyAuthUsernameTB.setFont(LABEL_FONT);
+    proxyAuthUsernameTB.setColumns(30);
+    proxyAuthPasswordTB.setFont(LABEL_FONT);
+    proxyAuthPasswordTB.setColumns(30);
 
     // Label for Port text box
     portLabel.setFont(LABEL_FONT);
     portLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-    portLabel.setText(MessageManager.getString("label.port"));
-
-    // Use proxy server checkbox
-    useProxy.setFont(LABEL_FONT);
-    useProxy.setHorizontalAlignment(SwingConstants.RIGHT);
-    useProxy.setHorizontalTextPosition(SwingConstants.LEADING);
-    useProxy.setText(MessageManager.getString("label.use_proxy_server"));
-    useProxy.addActionListener(new ActionListener()
+    portLabel.setText(MessageManager.getString("label.port") + ": ");
+    portLabel2.setFont(LABEL_FONT);
+    portLabel2.setHorizontalAlignment(SwingConstants.RIGHT);
+    portLabel2.setText(MessageManager.getString("label.port") + ": ");
+
+    httpLabel.setText("HTTP");
+    httpLabel.setFont(LABEL_FONT_BOLD);
+    httpLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    httpsLabel.setText("HTTPS");
+    httpsLabel.setFont(LABEL_FONT_BOLD);
+    httpsLabel.setHorizontalAlignment(SwingConstants.LEFT);
+
+    proxyAuthUsernameLabel
+            .setText(MessageManager.getString("label.username") + ": ");
+    proxyAuthUsernameLabel.setFont(LABEL_FONT);
+    proxyAuthUsernameLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    proxyAuthPasswordLabel
+            .setText(MessageManager.getString("label.password") + ": ");
+    proxyAuthPasswordLabel.setFont(LABEL_FONT);
+    proxyAuthPasswordLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+
+    // Proxy type radio buttons
+    noProxy.setFont(LABEL_FONT);
+    noProxy.setHorizontalAlignment(SwingConstants.LEFT);
+    noProxy.setText(MessageManager.getString("label.no_proxy"));
+    systemProxy.setFont(LABEL_FONT);
+    systemProxy.setHorizontalAlignment(SwingConstants.LEFT);
+    systemProxy.setText(MessageManager.formatMessage("label.system_proxy",
+            displayHostPort(Cache.startupProxyProperties[0],
+                    Cache.startupProxyProperties[1]),
+            displayHostPort(Cache.startupProxyProperties[2],
+                    Cache.startupProxyProperties[3])));
+    customProxy.setFont(LABEL_FONT);
+    customProxy.setHorizontalAlignment(SwingConstants.LEFT);
+    customProxy.setText(
+            MessageManager.getString("label.use_proxy_server") + ":");
+    ActionListener al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        proxyType_actionPerformed();
+      }
+    };
+    noProxy.addActionListener(al);
+    systemProxy.addActionListener(al);
+    customProxy.addActionListener(al);
+    proxyType.add(noProxy);
+    proxyType.add(systemProxy);
+    proxyType.add(customProxy);
+
+    proxyAuth.setFont(LABEL_FONT);
+    proxyAuth.setHorizontalAlignment(SwingConstants.LEFT);
+    proxyAuth.setText(MessageManager.getString("label.auth_required"));
+    proxyAuth.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        useProxy_actionPerformed();
+        proxyAuth_actionPerformed();
       }
     });
 
+    setCustomProxyEnabled();
+    setProxyAuthEnabled();
+
     // Make proxy server panel
     JPanel proxyPanel = new JPanel();
     TitledBorder titledBorder1 = new TitledBorder(
-            MessageManager.getString("label.proxy_server"));
+            MessageManager.getString("label.proxy_servers"));
     proxyPanel.setBorder(titledBorder1);
+    GridBagConstraints gbc = new GridBagConstraints();
     proxyPanel.setLayout(new GridBagLayout());
-    proxyPanel.add(serverLabel,
-            new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.NONE,
-                    new Insets(0, 2, 2, 0), 5, 0));
-    proxyPanel.add(portLabel,
-            new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.NONE,
-                    new Insets(0, 0, 2, 0), 11, 0));
-    proxyPanel.add(useProxy,
-            new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.NONE,
-                    new Insets(0, 2, 5, 185), 2, -4));
-    proxyPanel.add(proxyPortTB,
-            new GridBagConstraints(3, 1, 1, 1, 1.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-                    new Insets(0, 2, 2, 2), 54, 1));
-    proxyPanel.add(proxyServerTB,
-            new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0,
-                    GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-                    new Insets(0, 2, 2, 0), 263, 1));
+
+    gbc.gridx = 0;
+    gbc.gridy = 0;
+    gbc.weightx = 1.0;
+    gbc.gridheight = 1;
+    gbc.anchor = GridBagConstraints.WEST;
+    gbc.fill = GridBagConstraints.BOTH;
+
+    gbc.gridwidth = 5;
+    proxyPanel.add(noProxy, gbc);
+
+    gbc.gridy++;
+    proxyPanel.add(systemProxy, gbc);
+
+    gbc.gridy++;
+    proxyPanel.add(customProxy, gbc);
+
+    gbc.gridwidth = 1;
+    gbc.gridy++;
+    gbc.gridx = 0;
+    gbc.weightx = 0.1;
+    proxyPanel.add(httpLabel, gbc);
+
+    gbc.gridx++;
+    gbc.anchor = GridBagConstraints.EAST;
+    gbc.weightx = 0.15;
+    proxyPanel.add(serverLabel, gbc);
+
+    gbc.gridx++;
+    gbc.anchor = GridBagConstraints.WEST;
+    gbc.weightx = 0.5;
+    proxyPanel.add(proxyServerHttpTB, gbc);
+
+    gbc.gridx++;
+    gbc.anchor = GridBagConstraints.EAST;
+    gbc.weightx = 0.15;
+    proxyPanel.add(portLabel, gbc);
+
+    gbc.gridx++;
+    gbc.anchor = GridBagConstraints.WEST;
+    gbc.weightx = 0.1;
+    proxyPanel.add(proxyPortHttpTB, gbc);
+
+    gbc.gridy++;
+    gbc.gridx = 0;
+    gbc.anchor = GridBagConstraints.WEST;
+    gbc.weightx = 0.1;
+    proxyPanel.add(httpsLabel, gbc);
+
+    gbc.gridx++;
+    gbc.anchor = GridBagConstraints.EAST;
+    gbc.weightx = 0.15;
+    proxyPanel.add(serverLabel2, gbc);
+
+    gbc.gridx++;
+    gbc.anchor = GridBagConstraints.WEST;
+    gbc.weightx = 0.5;
+    proxyPanel.add(proxyServerHttpsTB, gbc);
+
+    gbc.gridx++;
+    gbc.anchor = GridBagConstraints.EAST;
+    gbc.weightx = 0.15;
+    proxyPanel.add(portLabel2, gbc);
+
+    gbc.gridx++;
+    gbc.anchor = GridBagConstraints.WEST;
+    gbc.weightx = 0.1;
+    proxyPanel.add(proxyPortHttpsTB, gbc);
+
+    gbc.gridy++;
+    gbc.gridx = 0;
+    gbc.gridwidth = 5;
+    gbc.anchor = GridBagConstraints.WEST;
+    gbc.weightx = 0.1;
+    proxyPanel.add(proxyAuth, gbc);
+
+    gbc.gridy++;
+    gbc.gridx = 0;
+    gbc.gridwidth = 1;
+    gbc.anchor = GridBagConstraints.EAST;
+    proxyPanel.add(proxyAuthUsernameLabel, gbc);
+
+    gbc.gridx++;
+    gbc.gridwidth = 4;
+    gbc.anchor = GridBagConstraints.WEST;
+    proxyPanel.add(proxyAuthUsernameTB, gbc);
+
+    gbc.gridy++;
+    gbc.gridx = 0;
+    gbc.gridwidth = 1;
+    gbc.anchor = GridBagConstraints.EAST;
+    proxyPanel.add(proxyAuthPasswordLabel, gbc);
+
+    gbc.gridx++;
+    gbc.gridwidth = 4;
+    gbc.anchor = GridBagConstraints.WEST;
+    proxyPanel.add(proxyAuthPasswordTB, gbc);
 
     return proxyPanel;
   }
 
+  private String displayHostPort(String host, String port)
+  {
+    boolean hostBlank = (host == null || host.isEmpty());
+    boolean portBlank = (port == null || port.isEmpty());
+    if (hostBlank && portBlank)
+    {
+      return MessageManager.getString("label.none");
+    }
+
+    StringBuilder sb = new StringBuilder();
+    sb.append(hostBlank ? "" : host);
+    if (!portBlank)
+    {
+      sb.append(":");
+      sb.append(port);
+    }
+    return sb.toString();
+  }
+
   /**
    * Initialises the checkboxes in the Connections tab
    */
@@ -1184,7 +1424,7 @@ public class GPreferences extends JPanel
     structureTab.setBorder(new TitledBorder(
             MessageManager.getString("label.structure_options")));
     structureTab.setLayout(null);
-    final int width = 400;
+    final int width = 420;
     final int height = 22;
     final int lineSpacing = 25;
     int ypos = 15;
@@ -1232,13 +1472,19 @@ public class GPreferences extends JPanel
     viewerLabel.setFont(LABEL_FONT);
     viewerLabel.setHorizontalAlignment(SwingConstants.LEFT);
     viewerLabel.setText(MessageManager.getString("label.structure_viewer"));
-    viewerLabel.setBounds(new Rectangle(10, ypos, 200, height));
+    viewerLabel.setBounds(new Rectangle(10, ypos, 220, height));
     structureTab.add(viewerLabel);
 
+    /*
+     * add all external viewers as options here - check 
+     * when selected whether the program is installed
+     */
     structViewer.setFont(LABEL_FONT);
-    structViewer.setBounds(new Rectangle(160, ypos, 120, height));
+    structViewer.setBounds(new Rectangle(190, ypos, 120, height));
     structViewer.addItem(ViewerType.JMOL.name());
     structViewer.addItem(ViewerType.CHIMERA.name());
+    structViewer.addItem(ViewerType.CHIMERAX.name());
+    structViewer.addItem(ViewerType.PYMOL.name());
     structViewer.addActionListener(new ActionListener()
     {
       @Override
@@ -1251,35 +1497,38 @@ public class GPreferences extends JPanel
     structureTab.add(structViewer);
 
     ypos += lineSpacing;
-    JLabel pathLabel = new JLabel();
-    pathLabel.setFont(new java.awt.Font("SansSerif", 0, 11));
-    pathLabel.setHorizontalAlignment(SwingConstants.LEFT);
-    pathLabel.setText(MessageManager.getString("label.chimera_path"));
-    pathLabel.setBounds(new Rectangle(10, ypos, 140, height));
-    structureTab.add(pathLabel);
-
-    chimeraPath.setFont(LABEL_FONT);
-    chimeraPath.setText("");
+    structureViewerPathLabel = new JLabel();
+    structureViewerPathLabel.setFont(LABEL_FONT);// new Font("SansSerif", 0, 11));
+    structureViewerPathLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    structureViewerPathLabel.setText(MessageManager
+            .formatMessage("label.viewer_path", "Chimera(X)"));
+    structureViewerPathLabel.setBounds(new Rectangle(10, ypos, 170, height));
+    structureViewerPathLabel.setEnabled(false);
+    structureTab.add(structureViewerPathLabel);
+
+    structureViewerPath.setFont(LABEL_FONT);
+    structureViewerPath.setText("");
+    structureViewerPath.setEnabled(false);
     final String tooltip = JvSwingUtils.wrapTooltip(true,
-            MessageManager.getString("label.chimera_path_tip"));
-    chimeraPath.setToolTipText(tooltip);
-    chimeraPath.setBounds(new Rectangle(160, ypos, 300, height));
-    chimeraPath.addMouseListener(new MouseAdapter()
+            MessageManager.getString("label.viewer_path_tip"));
+    structureViewerPath.setToolTipText(tooltip);
+    structureViewerPath.setBounds(new Rectangle(190, ypos, 290, height));
+    structureViewerPath.addMouseListener(new MouseAdapter()
     {
       @Override
       public void mouseClicked(MouseEvent e)
       {
-        if (e.getClickCount() == 2)
+        if (structureViewerPath.isEnabled() && e.getClickCount() == 2)
         {
           String chosen = openFileChooser();
           if (chosen != null)
           {
-            chimeraPath.setText(chosen);
+            structureViewerPath.setText(chosen);
           }
         }
       }
     });
-    structureTab.add(chimeraPath);
+    structureTab.add(structureViewerPath);
 
     ypos += lineSpacing;
     nwMapping.setFont(LABEL_FONT);
@@ -1294,7 +1543,7 @@ public class GPreferences extends JPanel
             MessageManager.getString("label.mapping_method"));
     mmTitledBorder.setTitleFont(LABEL_FONT);
     mappingPanel.setBorder(mmTitledBorder);
-    mappingPanel.setBounds(new Rectangle(10, ypos, 452, 45));
+    mappingPanel.setBounds(new Rectangle(10, ypos, 472, 45));
     // GridLayout mappingLayout = new GridLayout();
     mappingPanel.setLayout(new GridLayout());
     mappingPanel.add(nwMapping);
@@ -1305,9 +1554,20 @@ public class GPreferences extends JPanel
     ypos += lineSpacing;
     FTSDataColumnPreferences docFieldPref = new FTSDataColumnPreferences(
             PreferenceSource.PREFERENCES, PDBFTSRestClient.getInstance());
-    docFieldPref.setBounds(new Rectangle(10, ypos, 450, 120));
+    docFieldPref.setBounds(new Rectangle(10, ypos, 470, 120));
     structureTab.add(docFieldPref);
 
+    /*
+     * hide Chimera options in JalviewJS
+     */
+    if (Platform.isJS()) 
+    {
+      structureViewerPathLabel.setVisible(false);
+      structureViewerPath.setVisible(false);
+      viewerLabel.setVisible(false);
+      structViewer.setVisible(false);
+    }
+    
     return structureTab;
   }
 
@@ -1684,6 +1944,13 @@ public class GPreferences extends JPanel
     visualTab.add(fontNameCB);
     visualTab.add(fontSizeCB);
     visualTab.add(fontStyleCB);
+    
+    if (Platform.isJS())
+    {
+      startupCheckbox.setVisible(false);
+      startupFileTextfield.setVisible(false);
+    }
+    
     return visualTab;
   }
 
@@ -1693,27 +1960,50 @@ public class GPreferences extends JPanel
 
   protected void loadLastSavedBackupsOptions()
   {
+    BackupFilesPresetEntry savedPreset = BackupFilesPresetEntry
+            .getSavedBackupEntry();
     enableBackupFiles
-            .setSelected(Cache.getDefault(BackupFiles.ENABLED, true));
+            .setSelected(Cache.getDefault(BackupFiles.ENABLED, !Platform.isJS()));
+
+    BackupFilesPresetEntry backupfilesCustomEntry = BackupFilesPresetEntry
+            .createBackupFilesPresetEntry(Cache
+                    .getDefault(BackupFilesPresetEntry.CUSTOMCONFIG, null));
+    if (backupfilesCustomEntry == null)
+    {
+      backupfilesCustomEntry = BackupFilesPresetEntry.backupfilesPresetEntriesValues
+              .get(BackupFilesPresetEntry.BACKUPFILESSCHEMEDEFAULT);
+    }
+    BackupFilesPresetEntry.backupfilesPresetEntriesValues.put(
+            BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM,
+            backupfilesCustomEntry);
+
     setComboIntStringKey(backupfilesPresetsCombo,
-            Cache.getDefault(BackupFiles.NS + "_PRESET", 1));
-    suffixTemplate.setText(Cache.getDefault(BackupFiles.SUFFIX,
-            ".bak" + BackupFiles.NUM_PLACEHOLDER));
-    suffixDigitsSpinner
-            .setValue(Cache.getDefault(BackupFiles.SUFFIX_DIGITS, 3));
-    suffixReverse.setSelected(
-            Cache.getDefault(BackupFiles.REVERSE_ORDER, false));
-    backupfilesKeepAll
-            .setSelected(Cache.getDefault(BackupFiles.NO_MAX, false));
-    backupfilesRollMaxSpinner
-            .setValue(Cache.getDefault(BackupFiles.ROLL_MAX, 3));
-    backupfilesConfirmDelete.setSelected(
-            Cache.getDefault(BackupFiles.CONFIRM_DELETE_OLD, true));
+            Cache.getDefault(BackupFiles.NS + "_PRESET",
+                    BackupFilesPresetEntry.BACKUPFILESSCHEMEDEFAULT));
+
+    backupsSetOptions(savedPreset);
 
     backupsOptionsSetEnabled();
     updateBackupFilesExampleLabel();
   }
 
+  private boolean warnAboutSuffixReverseChange()
+  {
+    BackupFilesPresetEntry bfpe = BackupFilesPresetEntry
+            .getSavedBackupEntry();
+    boolean savedSuffixReverse = bfpe.reverse;
+    int savedSuffixDigits = bfpe.digits;
+    String savedSuffixTemplate = bfpe.suffix;
+
+    boolean nowSuffixReverse = suffixReverse.isSelected();
+    int nowSuffixDigits = getSpinnerInt(suffixDigitsSpinner, 3);
+    String nowSuffixTemplate = suffixTemplate.getText();
+    return nowSuffixReverse != savedSuffixReverse
+            && nowSuffixDigits == savedSuffixDigits
+            && nowSuffixTemplate != null
+            && nowSuffixTemplate.equals(savedSuffixTemplate);
+  }
+
   /**
    * Initialises the Backups tabbed panel.
    * 
@@ -1727,9 +2017,9 @@ public class GPreferences extends JPanel
     backupsTab.setLayout(new GridBagLayout());
 
     GridBagConstraints gbc = new GridBagConstraints();
-    gbc.weightx = 1.0;
+    gbc.weightx = 0.0;
     gbc.weighty = 0.0;
-    gbc.anchor = GridBagConstraints.NORTHWEST;
+    gbc.anchor = GridBagConstraints.FIRST_LINE_START;
     gbc.fill = GridBagConstraints.NONE;
 
     initBackupsTabPresetsPanel();
@@ -1737,7 +2027,6 @@ public class GPreferences extends JPanel
     initBackupsTabKeepFilesPanel();
     initBackupsTabFilenameExamplesPanel();
 
-    gbc.weightx = 0.5;
     enableBackupFiles.setFont(LABEL_FONT_BOLD);
     enableBackupFiles
             .setText(MessageManager.getString("label.enable_backupfiles"));
@@ -1746,44 +2035,55 @@ public class GPreferences extends JPanel
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        boolean selected = enableBackupFiles.isSelected();
         // enable other options only when the first is checked
         backupsOptionsSetEnabled();
       }
     });
 
-    // enable checkbox 1 row
+
+    // enable checkbox 1 col
     gbc.gridwidth = 1;
+    gbc.gridheight = 1;
     gbc.gridx = 0;
-    gbc.gridy = 0;
+    gbc.gridy = 0; // row 0
     backupsTab.add(enableBackupFiles, gbc);
 
+    // summary of scheme box (over two rows)
     gbc.gridx = 1;
-    gbc.weightx = 1.0;
+    gbc.weightx = 0.0;
     gbc.gridheight = 2;
-    // gbc.anchor = GridBagConstraints.NORTHEAST;
-    // gbc.fill = GridBagConstraints.HORIZONTAL;
+    gbc.anchor = GridBagConstraints.FIRST_LINE_END;
+    gbc.fill = GridBagConstraints.BOTH;
     backupsTab.add(exampleFilesPanel, gbc);
+    gbc.gridheight = 1;
+    gbc.anchor = GridBagConstraints.FIRST_LINE_START;
+    gbc.fill = GridBagConstraints.NONE;
+
+    // fill empty space on right
+    gbc.gridx++;
+    gbc.weightx = 1.0;
+    backupsTab.add(new JPanel(), gbc);
 
+    // schemes box
+    gbc.weightx = 0.0;
     gbc.gridx = 0;
-    gbc.gridy = 1;
+    gbc.gridy++; // row 1
     backupsTab.add(presetsPanel, gbc);
 
-    gbc.anchor = GridBagConstraints.NORTHWEST;
-    gbc.fill = GridBagConstraints.NONE;
+    // now using whole row
     gbc.gridwidth = 2;
     gbc.gridheight = 1;
-    // whole suffix panel next row
+    // keep files box
     gbc.gridx = 0;
-    gbc.gridy = 2;
+    gbc.gridy++; // row 2
     backupsTab.add(keepfilesPanel, gbc);
 
-    // keep files panel
-    gbc.gridy = 3;
+    // filename strategy box
+    gbc.gridy++; // row 3
     backupsTab.add(suffixPanel, gbc);
 
     // fill empty space
-    gbc.gridy = 4;
+    gbc.gridy++; // row 4
     gbc.weighty = 1.0;
     backupsTab.add(new JPanel(), gbc);
 
@@ -1791,130 +2091,230 @@ public class GPreferences extends JPanel
     return backupsTab;
   }
 
-  private final IntKeyStringValueEntry[] backupfilesPresetEntries = {
-      new IntKeyStringValueEntry(1,
-              MessageManager.getString("label.default")),
-      new IntKeyStringValueEntry(2,
-              MessageManager.getString("label.single_file")),
-      new IntKeyStringValueEntry(3,
-              MessageManager.getString("label.keep_all_versions")),
-      new IntKeyStringValueEntry(4,
-              MessageManager.getString("label.rolled_backups")),
-      // ...
-      new IntKeyStringValueEntry(255,
-              MessageManager.getString("label.previously_saved_scheme")),
-      // IMPORTANT, keep "Custom" entry with key 0 (even though it appears last)
-      new IntKeyStringValueEntry(0,
-              MessageManager.getString("label.customise")) };
-
-  private final Map<Integer, BackupFilesPresetEntry> backupfilesPresetEntriesValues = new HashMap<Integer, BackupFilesPresetEntry>()
-  {
-    {
-      put(1, new BackupFilesPresetEntry(
-              ".bak" + BackupFiles.NUM_PLACEHOLDER, 3, false, false, 3,
-              false));
-      put(2, new BackupFilesPresetEntry("~", 1, false, false, 1, false));
-      put(3, new BackupFilesPresetEntry(".v" + BackupFiles.NUM_PLACEHOLDER,
-              3, false, true, 10, true));
-      put(4, new BackupFilesPresetEntry(
-              "_bak." + BackupFiles.NUM_PLACEHOLDER, 1, true, false, 9,
-              false));
-    }
-  };
-
   private JPanel initBackupsTabPresetsPanel()
   {
-    presetsPanel.setBorder(
-            new TitledBorder(MessageManager.getString("label.schemes")));
 
-    for (int i = 0; i < backupfilesPresetEntries.length; i++)
+    String title = MessageManager.getString("label.schemes");
+
+    presetsPanel.setLayout(new GridBagLayout());
+
+    GridBagConstraints gbc = new GridBagConstraints();
+    gbc.weightx = 0.0;
+    gbc.weighty = 0.0;
+    gbc.anchor = GridBagConstraints.BASELINE_LEADING;
+    gbc.fill = GridBagConstraints.NONE;
+    gbc.gridwidth = 1;
+    gbc.gridheight = 1;
+
+    // "Scheme: "
+    gbc.gridx = 0;
+    gbc.gridy = 0;
+
+    presetsComboLabel = new JLabel(title + ":");
+    presetsPanel.add(presetsComboLabel, gbc);
+
+    List<Object> entries = Arrays
+            .asList((Object[]) BackupFilesPresetEntry.backupfilesPresetEntries);
+    List<String> tooltips = Arrays.asList(
+            BackupFilesPresetEntry.backupfilesPresetEntryDescriptions);
+    backupfilesPresetsCombo = JvSwingUtils.buildComboWithTooltips(entries,
+            tooltips);
+    /*
+    for (int i = 0; i < BackupFilesPresetEntry.backupfilesPresetEntries.length; i++)
     {
-      backupfilesPresetsCombo.addItem(backupfilesPresetEntries[i]);
+      backupfilesPresetsCombo
+              .addItem(BackupFilesPresetEntry.backupfilesPresetEntries[i]);
     }
+    */
 
-    // put "Previously saved scheme" item in italics (it's not really
-    // selectable, as such -- it deselects itself when selected) and
-    // "Customise" in bold
-    backupfilesPresetsCombo
-            .setRenderer(new BackupFilesPresetsComboBoxRenderer());
     backupfilesPresetsCombo.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        backupsTabUpdatePresets();
+        int key = getComboIntStringKey(backupfilesPresetsCombo);
+        if (!customiseCheckbox.isSelected())
+        {
+          backupfilesPresetsComboLastSelected = key;
+        }
+        if (key == BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM)
+        {
+          if (customiseCheckbox.isSelected())
+          {
+            // got here by clicking on customiseCheckbox so don't change the values
+            backupfilesCustomOptionsSetEnabled();
+          }
+          else
+          {
+            backupsTabUpdatePresets();
+            backupfilesCustomOptionsSetEnabled();
+          }
+        }
+        else
+        {
+          customiseCheckbox.setSelected(false);
+          backupsTabUpdatePresets();
+          backupfilesCustomOptionsSetEnabled();
+        }
+      }
+    });
+
+    // dropdown list of preset schemes
+    gbc.gridx = 1;
+    presetsPanel.add(backupfilesPresetsCombo, gbc);
+
+    revertButton.setText(MessageManager.getString("label.cancel_changes"));
+    revertButton.setToolTipText(
+            MessageManager.getString("label.cancel_changes_description"));
+    revertButton.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        backupsSetOptions(
+                BackupFilesPresetEntry.backupfilesPresetEntriesValues.get(
+                        BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM));
+        backupfilesCustomOptionsSetEnabled();
+      }
+
+    });
+    revertButton.setFont(LABEL_FONT);
+
+    customiseCheckbox.setFont(LABEL_FONT);
+    customiseCheckbox.setText(MessageManager.getString("label.customise"));
+    customiseCheckbox.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        int currently = getComboIntStringKey(backupfilesPresetsCombo);
+        if (customiseCheckbox.isSelected())
+        {
+          backupfilesPresetsComboLastSelected = currently;
+          setComboIntStringKey(backupfilesPresetsCombo,
+                  BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM);
+        }
+        else
+        {
+          setComboIntStringKey(backupfilesPresetsCombo,
+                  backupfilesPresetsComboLastSelected);
+
+        }
+        backupfilesCustomOptionsSetEnabled();
       }
     });
+    customiseCheckbox.setToolTipText(
+            MessageManager.getString("label.customise_description"));
+
+    // customise checkbox
+    gbc.gridx = 0;
+    gbc.gridy++;
+    presetsPanel.add(customiseCheckbox, gbc);
+
+    // "Cancel changes" button (aligned with combo box above)
+    gbc.gridx = 1;
+    presetsPanel.add(revertButton, gbc);
 
-    presetsPanel.add(backupfilesPresetsCombo);
     return presetsPanel;
   }
 
+  private JPanel initBackupsTabFilenameExamplesPanel()
+  {
+    String title = MessageManager
+            .getString("label.scheme_examples");
+    TitledBorder tb = new TitledBorder(title);
+    exampleFilesPanel.setBorder(tb);
+    exampleFilesPanel.setLayout(new GridBagLayout());
+
+
+    backupfilesExampleLabel.setEditable(false);
+    backupfilesExampleLabel
+            .setBackground(exampleFilesPanel.getBackground());
+
+    updateBackupFilesExampleLabel();
+
+    GridBagConstraints gbc = new GridBagConstraints();
+    gbc.weightx = 1.0;
+    gbc.weighty = 1.0;
+    gbc.fill = GridBagConstraints.NONE;
+    gbc.anchor = GridBagConstraints.FIRST_LINE_START;
+
+    exampleFilesPanel.add(backupfilesExampleLabel, gbc);
+    return exampleFilesPanel;
+  }
+
   private void backupsTabUpdatePresets()
   {
     IntKeyStringValueEntry entry = (IntKeyStringValueEntry) backupfilesPresetsCombo
             .getSelectedItem();
-    int key = entry.getKey();
-    String value = entry.getValue();
-    // 0 reserved for "Custom" and 255 reserved or "Previously saved scheme"
-    boolean special = key == 0 || key == 255;
+    int key = entry.k;
+    String value = entry.v;
 
-    if (special)
+    if (BackupFilesPresetEntry.backupfilesPresetEntriesValues
+            .containsKey(key))
     {
-      if (key == 0)
-      {
-        // backupsSetCustomOptions();
-      }
-      else if (key == 255)
-      {
-        loadLastSavedBackupsOptions();
-        // switch to "Customise" scheme? probably not
-        // setComboIntStringKey(backupfilesPresetsCombo, 0);
-      }
+      backupsSetOptions(
+              BackupFilesPresetEntry.backupfilesPresetEntriesValues
+                      .get(key));
     }
     else
     {
-      if (backupfilesPresetEntriesValues.containsKey(key))
-      {
-        backupsSetOptions(backupfilesPresetEntriesValues.get(key));
-      }
-      else
-      {
-        System.out.println("Preset '" + value + "' not implemented");
-      }
+      Cache.log.error(
+              "Preset '" + value + "' [key:" + key + "] not implemented");
     }
 
-    backupfilesCustomOptionsSetEnabled();
+    // Custom options will now be enabled when the customiseCheckbox is checked
+    // (performed above)
+    // backupfilesCustomOptionsSetEnabled();
     updateBackupFilesExampleLabel();
   }
 
-  protected int getComboIntStringKey(JComboBox c)
+  protected int getComboIntStringKey(
+          JComboBox<Object> backupfilesPresetsCombo2)
   {
-    IntKeyStringValueEntry e = (IntKeyStringValueEntry) c.getSelectedItem();
-    return e != null ? e.getKey() : 0;
+    IntKeyStringValueEntry e;
+    try
+    {
+      e = (IntKeyStringValueEntry) backupfilesPresetsCombo2
+              .getSelectedItem();
+    } catch (Exception ex)
+    {
+      Cache.log.error(
+              "Problem casting Combo entry to IntKeyStringValueEntry.");
+      e = null;
+    }
+    return e != null ? e.k : 0;
   }
 
-  protected void setComboIntStringKey(JComboBox<IntKeyStringValueEntry> c,
+  protected void setComboIntStringKey(
+          JComboBox<Object> backupfilesPresetsCombo2,
           int key)
   {
-    for (int i = 0; i < c.getItemCount(); i++)
+    for (int i = 0; i < backupfilesPresetsCombo2.getItemCount(); i++)
     {
-      IntKeyStringValueEntry e = c.getItemAt(i);
-      int k = e.getKey();
-      String v = e.getValue();
-      if (e.getKey() == key)
+      IntKeyStringValueEntry e;
+      try
+      {
+        e = (IntKeyStringValueEntry) backupfilesPresetsCombo2.getItemAt(i);
+      } catch (Exception ex)
+      {
+        Cache.log.error(
+                "Problem casting Combo entry to IntKeyStringValueEntry. Skipping item. ");
+        continue;
+      }
+      if (e.k == key)
       {
-        c.setSelectedIndex(i);
+        backupfilesPresetsCombo2.setSelectedIndex(i);
         break;
       }
     }
-    backupsTabUpdatePresets();
+    // backupsTabUpdatePresets();
   }
 
   private JPanel initBackupsTabSuffixPanel()
   {
     suffixPanel.setBorder(new TitledBorder(
-            MessageManager.getString("label.backup_filenames")));
+            MessageManager.getString("label.backup_filename_strategy")));
     suffixPanel.setLayout(new GridBagLayout());
 
     suffixTemplateLabel
@@ -1932,16 +2332,18 @@ public class GPreferences extends JPanel
       {
         updateBackupFilesExampleLabel();
         backupfilesCustomOptionsSetEnabled();
+        backupfilesRevertButtonSetEnabled(true);
       }
 
     });
-    KeyListener kl = new KeyListener()
+    suffixTemplate.addKeyListener(new KeyListener()
     {
       @Override
       public void keyReleased(KeyEvent e)
       {
         updateBackupFilesExampleLabel();
         backupfilesCustomOptionsSetEnabled();
+        backupfilesRevertButtonSetEnabled(true);
       }
 
       @Override
@@ -1961,26 +2363,25 @@ public class GPreferences extends JPanel
         }
       }
 
-    };
-    suffixTemplate.addKeyListener(kl);
+    });
 
     // digits spinner
     suffixDigitsLabel
             .setText(MessageManager.getString("label.index_digits"));
     suffixDigitsLabel.setHorizontalAlignment(SwingConstants.LEFT);
     suffixDigitsLabel.setFont(LABEL_FONT);
-    int defaultmin = 1;
-    int defaultmax = 6;
     ChangeListener c = new ChangeListener()
     {
       @Override
       public void stateChanged(ChangeEvent e)
       {
+        backupfilesRevertButtonSetEnabled(true);
         updateBackupFilesExampleLabel();
       }
 
     };
-    setIntegerSpinner(suffixDigitsSpinner, defaultmin, defaultmax, 3, c);
+    setIntegerSpinner(suffixDigitsSpinner, BackupFilesPresetEntry.DIGITSMIN,
+            BackupFilesPresetEntry.DIGITSMAX, 3, c);
 
     suffixReverse.setLabels(MessageManager.getString("label.reverse_roll"),
             MessageManager.getString("label.increment_index"));
@@ -1989,7 +2390,23 @@ public class GPreferences extends JPanel
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        updateBackupFilesExampleLabel();
+        boolean okay = true;
+        if (warnAboutSuffixReverseChange())
+        {
+          // Warning popup
+          okay = confirmSuffixReverseChange();
+        }
+        if (okay)
+        {
+          backupfilesRevertButtonSetEnabled(true);
+          updateBackupFilesExampleLabel();
+        }
+        else
+        {
+          boolean savedSuffixReverse = BackupFilesPresetEntry
+                  .getSavedBackupEntry().reverse;
+          suffixReverse.setSelected(savedSuffixReverse);
+        }
       }
     });
 
@@ -2034,6 +2451,20 @@ public class GPreferences extends JPanel
     return suffixPanel;
   }
 
+  private boolean confirmSuffixReverseChange()
+  {
+    boolean ret = false;
+    String warningMessage = MessageManager
+            .getString("label.warning_confirm_change_reverse");
+    int confirm = JvOptionPane.showConfirmDialog(Desktop.desktop,
+            warningMessage,
+            MessageManager.getString("label.change_increment_decrement"),
+            JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE);
+
+    ret = (confirm == JvOptionPane.YES_OPTION);
+    return ret;
+  }
+
   private JPanel initBackupsTabKeepFilesPanel()
   {
     keepfilesPanel.setBorder(
@@ -2049,6 +2480,7 @@ public class GPreferences extends JPanel
       @Override
       public void actionPerformed(ActionEvent e)
       {
+        backupfilesRevertButtonSetEnabled(true);
         updateBackupFilesExampleLabel();
       }
     });
@@ -2057,7 +2489,7 @@ public class GPreferences extends JPanel
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        boolean selected = backupfilesKeepAll.isSelected();
+        backupfilesRevertButtonSetEnabled(true);
         keepRollMaxOptionsEnabled();
         updateBackupFilesExampleLabel();
       }
@@ -2068,15 +2500,26 @@ public class GPreferences extends JPanel
       @Override
       public void stateChanged(ChangeEvent e)
       {
+        backupfilesRevertButtonSetEnabled(true);
         updateBackupFilesExampleLabel();
       }
 
     };
-    setIntegerSpinner(backupfilesRollMaxSpinner, 1, 999, 4, true, c);
+    setIntegerSpinner(backupfilesRollMaxSpinner,
+            BackupFilesPresetEntry.ROLLMAXMIN,
+            BackupFilesPresetEntry.ROLLMAXMAX, 4, true, c);
 
     backupfilesConfirmDelete.setLabels(
-            MessageManager.getString("label.confirm_delete"),
+            MessageManager.getString("label.always_ask"),
             MessageManager.getString("label.auto_delete"));
+    backupfilesConfirmDelete.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        backupfilesRevertButtonSetEnabled(true);
+      }
+    });
     // update the enabled section
     keepRollMaxOptionsEnabled();
 
@@ -2112,13 +2555,6 @@ public class GPreferences extends JPanel
     kgbc.gridwidth = GridBagConstraints.REMAINDER;
     kgbc.fill = GridBagConstraints.HORIZONTAL;
     kgbc.weightx = 1.0;
-    /*
-    keepfilesPanel.add(backupfilesConfirmDelete.getTrueButton(), kgbc);
-    
-    // fourth row (indented)
-    kgbc.gridy = 3;
-    keepfilesPanel.add(backupfilesConfirmDelete.getFalseButton(), kgbc);
-    */
 
     JPanel jp = new JPanel();
     jp.setLayout(new FlowLayout());
@@ -2135,42 +2571,10 @@ public class GPreferences extends JPanel
     return keepfilesPanel;
   }
 
-  private JPanel initBackupsTabFilenameExamplesPanel()
-  {
-    String title = MessageManager
-            .getString("label.summary_of_backups_scheme");
-    TitledBorder tb = new TitledBorder(new EmptyBorder(0, 0, 0, 0), title);
-    // tb.setTitleFont(LABEL_FONT);
-    exampleFilesPanel.setBorder(tb);
-    exampleFilesPanel.setLayout(new GridBagLayout());
-
-    // inordinately difficult to get this titledborder the right size for the
-    // title
-
-    backupfilesExampleLabel.setEditable(false);
-    backupfilesExampleLabel.setPreferredSize(new Dimension(280, 85));
-    backupfilesExampleLabel.setAlignmentX(LEFT_ALIGNMENT);
-    backupfilesExampleLabel.setAlignmentY(TOP_ALIGNMENT);
-
-    backupfilesExampleLabel.setFont(LABEL_FONT_ITALIC);
-    backupfilesExampleLabel
-            .setBackground(exampleFilesPanel.getBackground());
-
-    updateBackupFilesExampleLabel();
-    exampleFilesPanel.setMinimumSize(new Dimension(280, 85));
-
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.anchor = GridBagConstraints.EAST;
-
-    gbc.gridy = 0;
-    exampleFilesPanel.add(backupfilesExampleLabel, gbc);
-    return exampleFilesPanel;
-  }
-
   protected void updateBackupFilesExampleLabel()
   {
     int exampleindex = 12;
-    String base = MessageManager.getString("label.filename");
+    String base = MessageManager.getString("label.filename") + ".fa";
     if (base == null || base.length() == 0)
     {
       base = "file_name.fa";
@@ -2191,37 +2595,22 @@ public class GPreferences extends JPanel
       return;
     }
 
-    try
-    {
-      backupfilesRollMaxSpinner.commitEdit();
-      rollMax = (Integer) backupfilesRollMaxSpinner.getValue();
-      rollMax = rollMax < 1 ? 1 : rollMax;
-    } catch (Exception e)
-    {
-      System.out.println(
-              "Failed casting (Integer) backupfilesRollMaxSpinner.getValue()");
-    }
+    rollMax = getSpinnerInt(backupfilesRollMaxSpinner, 4);
+    rollMax = rollMax < 1 ? 1 : rollMax;
 
     if (suffix.indexOf(BackupFiles.NUM_PLACEHOLDER) == -1)
     {
       rollMax = 1;
     }
 
-    try
-    {
-      suffixDigitsSpinner.commitEdit();
-      digits = (Integer) suffixDigitsSpinner.getValue();
-      digits = digits < 1 ? 1 : digits;
-    } catch (Exception e)
-    {
-      System.out.println(
-              "Failed casting (Integer) suffixTemplateSpinner.getValue()");
-    }
+    digits = getSpinnerInt(suffixDigitsSpinner, 3);
+    digits = digits < 1 ? 1 : digits;
 
     int lowersurround = 2;
     int uppersurround = 0;
     StringBuilder exampleSB = new StringBuilder();
     boolean firstLine = true;
+    int lineNumber = 0;
     if (reverse)
     {
 
@@ -2232,6 +2621,7 @@ public class GPreferences extends JPanel
         if (index == min + lowersurround && index < max - uppersurround - 1)
         {
           exampleSB.append("\n...");
+          lineNumber++;
         }
         else if (index > min + lowersurround && index < max - uppersurround)
         {
@@ -2246,8 +2636,9 @@ public class GPreferences extends JPanel
           else
           {
             exampleSB.append("\n");
+            lineNumber++;
           }
-          exampleSB.append(BackupFilenameFilter.getBackupFilename(index,
+          exampleSB.append(BackupFilenameParts.getBackupFilename(index,
                   base, suffix, digits));
           if (min == max)
           {
@@ -2285,6 +2676,7 @@ public class GPreferences extends JPanel
         if (index == min + lowersurround && index < max - uppersurround - 1)
         {
           exampleSB.append("\n...");
+          lineNumber++;
         }
         else if (index > min + lowersurround && index < max - uppersurround)
         {
@@ -2299,8 +2691,9 @@ public class GPreferences extends JPanel
           else
           {
             exampleSB.append("\n");
+            lineNumber++;
           }
-          exampleSB.append(BackupFilenameFilter.getBackupFilename(index,
+          exampleSB.append(BackupFilenameParts.getBackupFilename(index,
                   base, suffix, digits));
           if (min == max)
           {
@@ -2327,6 +2720,18 @@ public class GPreferences extends JPanel
 
     }
 
+    // add some extra empty lines to pad out the example files box. ugh, please tell
+    // me how to do this better
+    int remainingLines = lowersurround + uppersurround + 1 - lineNumber;
+    if (remainingLines > 0)
+    {
+      for (int i = 0; i < remainingLines; i++)
+      {
+        exampleSB.append("\n ");
+        lineNumber++;
+      }
+    }
+
     backupfilesExampleLabel.setText(exampleSB.toString());
   }
 
@@ -2338,10 +2743,10 @@ public class GPreferences extends JPanel
     {
       try
       {
-        i = Integer.parseInt((String) s.getValue());
+        i = ((Integer) s.getValue()).intValue();
       } catch (Exception e)
       {
-        System.out.println(
+        Cache.log.error(
                 "Exception casting the initial value of s.getValue()");
       }
     }
@@ -2357,6 +2762,10 @@ public class GPreferences extends JPanel
     {
       max = def;
     }
+    if (def < min)
+    {
+      def = min;
+    }
     SpinnerModel sModel = new SpinnerNumberModel(def, min, max, 1);
     s.setModel(sModel);
 
@@ -2373,7 +2782,7 @@ public class GPreferences extends JPanel
       i = (Integer) s.getValue();
     } catch (Exception e)
     {
-      System.out.println("Failed casting (Integer) JSpinner s.getValue()");
+      Cache.log.error("Failed casting (Integer) JSpinner s.getValue()");
     }
     return i;
   }
@@ -2390,7 +2799,7 @@ public class GPreferences extends JPanel
   private void backupfilesKeepAllSetEnabled(boolean tryEnabled)
   {
     boolean enabled = tryEnabled && enableBackupFiles.isSelected()
-            && getComboIntStringKey(backupfilesPresetsCombo) == 0
+            && customiseCheckbox.isSelected()
             && suffixTemplate.getText()
                     .indexOf(BackupFiles.NUM_PLACEHOLDER) > -1;
     keepfilesPanel.setEnabled(enabled);
@@ -2411,18 +2820,48 @@ public class GPreferences extends JPanel
   private void backupfilesSuffixTemplateSetEnabled(boolean tryEnabled)
   {
     boolean enabled = tryEnabled && enableBackupFiles.isSelected()
-            && getComboIntStringKey(backupfilesPresetsCombo) == 0;
+            && customiseCheckbox.isSelected();
     suffixPanel.setEnabled(enabled);
     suffixTemplateLabel.setEnabled(enabled);
     suffixTemplate.setEnabled(enabled);
     backupfilesSuffixTemplateDigitsSetEnabled();
   }
 
+  private void backupfilesRevertButtonSetEnabled(boolean tryEnabled)
+  {
+    boolean enabled = tryEnabled && enableBackupFiles.isSelected()
+            && customiseCheckbox.isSelected() && backupfilesCustomChanged();
+    revertButton.setEnabled(enabled);
+  }
+
+  private boolean backupfilesCustomChanged()
+  {
+    BackupFilesPresetEntry custom = BackupFilesPresetEntry.backupfilesPresetEntriesValues
+            .get(BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM);
+    BackupFilesPresetEntry current = getBackupfilesCurrentEntry();
+    return !custom.equals(current);
+  }
+
+  protected BackupFilesPresetEntry getBackupfilesCurrentEntry()
+  {
+    String suffix = suffixTemplate.getText();
+    int digits = getSpinnerInt(suffixDigitsSpinner, 3);
+    boolean reverse = suffixReverse.isSelected();
+    boolean keepAll = backupfilesKeepAll.isSelected();
+    int rollMax = getSpinnerInt(backupfilesRollMaxSpinner, 3);
+    boolean confirmDelete = backupfilesConfirmDelete.isSelected();
+
+    BackupFilesPresetEntry bfpe = new BackupFilesPresetEntry(suffix, digits,
+            reverse, keepAll, rollMax, confirmDelete);
+
+    return bfpe;
+  }
+
   protected void backupfilesCustomOptionsSetEnabled()
   {
-    int scheme = getComboIntStringKey(backupfilesPresetsCombo);
-    boolean enabled = scheme == 0 && enableBackupFiles.isSelected();
+    boolean enabled = customiseCheckbox.isSelected();
 
+    backupfilesRevertButtonSetEnabled(enabled);
     backupfilesSuffixTemplateSetEnabled(enabled);
     backupfilesKeepAllSetEnabled(enabled);
   }
@@ -2438,7 +2877,10 @@ public class GPreferences extends JPanel
   {
     boolean enabled = enableBackupFiles.isSelected();
     presetsPanel.setEnabled(enabled);
+    presetsComboLabel.setEnabled(enabled);
     backupfilesPresetsCombo.setEnabled(enabled);
+    customiseCheckbox.setEnabled(enabled);
+    revertButton.setEnabled(enabled);
   }
 
   protected void backupsOptionsSetEnabled()
@@ -2570,13 +3012,40 @@ public class GPreferences extends JPanel
 
   }
 
-  public void useProxy_actionPerformed()
+  public void setProxyAuthEnabled()
   {
-    boolean enabled = useProxy.isSelected();
+    boolean enabled = proxyAuth.isSelected() && proxyAuth.isEnabled();
+    proxyAuthUsernameLabel.setEnabled(enabled);
+    proxyAuthPasswordLabel.setEnabled(enabled);
+    proxyAuthUsernameTB.setEnabled(enabled);
+    proxyAuthPasswordTB.setEnabled(enabled);
+  }
+
+  public void setCustomProxyEnabled()
+  {
+    boolean enabled = customProxy.isSelected();
     portLabel.setEnabled(enabled);
     serverLabel.setEnabled(enabled);
-    proxyServerTB.setEnabled(enabled);
-    proxyPortTB.setEnabled(enabled);
+    portLabel2.setEnabled(enabled);
+    serverLabel2.setEnabled(enabled);
+    httpLabel.setEnabled(enabled);
+    httpsLabel.setEnabled(enabled);
+    proxyServerHttpTB.setEnabled(enabled);
+    proxyPortHttpTB.setEnabled(enabled);
+    proxyServerHttpsTB.setEnabled(enabled);
+    proxyPortHttpsTB.setEnabled(enabled);
+    proxyAuth.setEnabled(enabled);
+    setProxyAuthEnabled();
+  }
+
+  public void proxyType_actionPerformed()
+  {
+    setCustomProxyEnabled();
+  }
+
+  public void proxyAuth_actionPerformed()
+  {
+    setProxyAuthEnabled();
   }
 
   /**
@@ -2652,83 +3121,3 @@ public class GPreferences extends JPanel
   }
 }
 
-class IntKeyStringValueEntry
-{
-  int k;
-
-  String v;
-
-  public IntKeyStringValueEntry(int k, String v)
-  {
-    this.k = k;
-    this.v = v;
-  }
-
-  @Override
-  public String toString()
-  {
-    return this.getValue();
-  }
-
-  public int getKey()
-  {
-    return k;
-  }
-
-  public String getValue()
-  {
-    return v;
-  }
-}
-
-class BackupFilesPresetEntry
-{
-  String suffix;
-
-  int digits;
-
-  boolean reverse;
-
-  boolean keepAll;
-
-  int rollMax;
-
-  boolean confirmDelete;
-
-  public BackupFilesPresetEntry(String suffix, int digits, boolean reverse,
-          boolean keepAll, int rollMax, boolean confirmDelete)
-  {
-    this.suffix = suffix;
-    this.digits = digits;
-    this.reverse = reverse;
-    this.keepAll = keepAll;
-    this.rollMax = rollMax;
-    this.confirmDelete = confirmDelete;
-  }
-}
-
-class BackupFilesPresetsComboBoxRenderer extends DefaultListCellRenderer
-{
-  @Override
-  public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
-    super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
-    
-    try {
-      IntKeyStringValueEntry e = (IntKeyStringValueEntry) value;
-      if (e.getKey() == 255)
-      {
-        // "Previously saved scheme" item
-        this.setFont(this.getFont().deriveFont(Font.ITALIC));
-      }
-      else if (e.getKey() == 0)
-      {
-        // "Customise" item
-        this.setFont(this.getFont().deriveFont(Font.BOLD));
-      }
-    } catch (Exception e) {
-      return this;
-    }
-
-    return this;
-  }
-}
\ No newline at end of file