JAL-3141 New layout and wording of Backups Preferences pane after consultation with...
[jalview.git] / src / jalview / jbgui / GPreferences.java
index e26b991..18dbcb4 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.jbgui;
 
+import jalview.bin.Cache;
 import jalview.fts.core.FTSDataColumnPreferences;
 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
 import jalview.fts.service.pdb.PDBFTSRestClient;
@@ -27,6 +28,7 @@ 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;
@@ -47,6 +49,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 javax.swing.AbstractCellEditor;
 import javax.swing.BorderFactory;
@@ -57,6 +61,7 @@ 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.JRadioButton;
 import javax.swing.JScrollPane;
@@ -91,6 +96,9 @@ public class GPreferences extends JPanel
   private static final Font LABEL_FONT_ITALIC = JvSwingUtils
           .getLabelFont(false, true);
 
+  private static final Font LABEL_FONT_BOLD = JvSwingUtils
+          .getLabelFont(true, false);
+
   /*
    * Visual tab components
    */
@@ -271,7 +279,6 @@ public class GPreferences extends JPanel
 
   protected JCheckBox sortByTree = new JCheckBox();
 
-
   /*
    * Web Services tab
    */
@@ -285,6 +292,10 @@ public class GPreferences extends JPanel
 
   protected JCheckBox enableBackupFiles = new JCheckBox();
 
+  protected JPanel presetsPanel = new JPanel();
+
+  protected JComboBox<IntKeyStringValueEntry> backupfilesPresetsCombo = new JComboBox<>();
+
   protected JPanel suffixPanel = new JPanel();
 
   protected JPanel keepfilesPanel = new JPanel();
@@ -303,7 +314,7 @@ public class GPreferences extends JPanel
 
   protected JalviewBooleanRadioButtons backupfilesKeepAll = new JalviewBooleanRadioButtons();
 
-  protected JSpinner backupfilesRollMaxSpinner = new JSpinner();
+  public JSpinner backupfilesRollMaxSpinner = new JSpinner();
 
   protected JLabel oldBackupFilesLabel = new JLabel();
 
@@ -521,11 +532,8 @@ public class GPreferences extends JPanel
     embbedBioJSON.setText(MessageManager.getString("label.embbed_biojson"));
     embbedBioJSON.setBounds(new Rectangle(228, 200, 250, 23));
 
-
     TitledBorder backupFilesBorder = new TitledBorder(
-            MessageManager
-                    .getString("label.backup_files"));
-
+            MessageManager.getString("label.backup_files"));
 
     jPanel11.add(jLabel1);
     jPanel11.add(blcjv);
@@ -1058,14 +1066,13 @@ public class GPreferences extends JPanel
         hiddenColour_actionPerformed(hiddenColour);
       }
     });
-    
+
     useLegacyGap = new JCheckBox(
             MessageManager.getString("label.ov_legacy_gap"));
     useLegacyGap.setFont(LABEL_FONT);
     useLegacyGap.setHorizontalAlignment(SwingConstants.LEFT);
     useLegacyGap.setVerticalTextPosition(SwingConstants.TOP);
-    gapLabel = new JLabel(
-            MessageManager.getString("label.gap_colour"));
+    gapLabel = new JLabel(MessageManager.getString("label.gap_colour"));
     gapLabel.setFont(LABEL_FONT);
     gapLabel.setHorizontalAlignment(SwingConstants.LEFT);
     gapLabel.setVerticalTextPosition(SwingConstants.TOP);
@@ -1681,6 +1688,33 @@ public class GPreferences extends JPanel
   }
 
   /**
+   * Load the saved Backups options EXCEPT "Enabled" and "Scheme"
+   */
+
+  protected void loadLastSavedBackupsOptions()
+  {
+    enableBackupFiles
+            .setSelected(Cache.getDefault(BackupFiles.ENABLED, true));
+    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));
+
+    backupsOptionsSetEnabled();
+    updateBackupFilesExampleLabel();
+  }
+
+  /**
    * Initialises the Backups tabbed panel.
    * 
    * @return
@@ -1688,13 +1722,25 @@ public class GPreferences extends JPanel
   private JPanel initBackupsTab()
   {
     JPanel backupsTab = new JPanel();
-    // backupsTab.setBorder(new TitledBorder(MessageManager
-    // .getString("label.backup_files")));
+    backupsTab.setBorder(new TitledBorder(
+            MessageManager.getString("label.backup_files")));
     backupsTab.setLayout(new GridBagLayout());
 
-    enableBackupFiles.setFont(LABEL_FONT);
-    enableBackupFiles.setText(
-            MessageManager.getString("label.enable_backupfiles"));
+    GridBagConstraints gbc = new GridBagConstraints();
+    gbc.weightx = 1.0;
+    gbc.weighty = 0.0;
+    gbc.anchor = GridBagConstraints.NORTHWEST;
+    gbc.fill = GridBagConstraints.NONE;
+
+    initBackupsTabPresetsPanel();
+    initBackupsTabSuffixPanel();
+    initBackupsTabKeepFilesPanel();
+    initBackupsTabFilenameExamplesPanel();
+
+    gbc.weightx = 0.5;
+    enableBackupFiles.setFont(LABEL_FONT_BOLD);
+    enableBackupFiles
+            .setText(MessageManager.getString("label.enable_backupfiles"));
     enableBackupFiles.addActionListener(new ActionListener()
     {
       @Override
@@ -1702,76 +1748,182 @@ public class GPreferences extends JPanel
       {
         boolean selected = enableBackupFiles.isSelected();
         // enable other options only when the first is checked
-        backupsOptionsSetEnabled(selected);
+        backupsOptionsSetEnabled();
       }
     });
 
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.weightx = 1.0;
-    gbc.weighty = 0.0;
-    gbc.anchor = GridBagConstraints.NORTHWEST;
-    gbc.fill = GridBagConstraints.NONE;
-
     // enable checkbox 1 row
     gbc.gridwidth = 1;
+    gbc.gridx = 0;
     gbc.gridy = 0;
     backupsTab.add(enableBackupFiles, gbc);
 
-    initBackupsTabSuffixPanel();
-    initBackupsTabKeepFilesPanel();
-    initBackupsTabFilenameExamplesPanel();
-
-    /*
-    // set widths of the panels to the max one
-    int maxw = suffixPanel.getWidth();
-    if (maxw < keepfilesPanel.getWidth())
-    {
-      maxw = keepfilesPanel.getWidth();
-    }
-    if (maxw < exampleFilesPanel.getWidth())
-    {
-      maxw = exampleFilesPanel.getWidth();
-    }
-    suffixPanel.setSize(suffixPanel.getHeight(), maxw);
-    keepfilesPanel.setSize(keepfilesPanel.getHeight(), maxw);
-    exampleFilesPanel.setSize(exampleFilesPanel.getHeight(), maxw);
-    */
+    gbc.gridx = 1;
+    gbc.weightx = 1.0;
+    gbc.gridheight = 2;
+    // gbc.anchor = GridBagConstraints.NORTHEAST;
+    // gbc.fill = GridBagConstraints.HORIZONTAL;
+    backupsTab.add(exampleFilesPanel, gbc);
 
-    // whole suffix panel next row
+    gbc.gridx = 0;
     gbc.gridy = 1;
-    backupsTab.add(suffixPanel, gbc);
+    backupsTab.add(presetsPanel, gbc);
 
-    // keep files panel
+    gbc.anchor = GridBagConstraints.NORTHWEST;
+    gbc.fill = GridBagConstraints.NONE;
+    gbc.gridwidth = 2;
+    gbc.gridheight = 1;
+    // whole suffix panel next row
+    gbc.gridx = 0;
     gbc.gridy = 2;
     backupsTab.add(keepfilesPanel, gbc);
 
-    // whole examples panel next row
+    // keep files panel
     gbc.gridy = 3;
-    backupsTab.add(exampleFilesPanel, gbc);
+    backupsTab.add(suffixPanel, gbc);
 
     // fill empty space
     gbc.gridy = 4;
     gbc.weighty = 1.0;
     backupsTab.add(new JPanel(), gbc);
 
-    backupsOptionsSetEnabled(enableBackupFiles.isSelected());
+    backupsOptionsSetEnabled();
     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++)
+    {
+      backupfilesPresetsCombo.addItem(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();
+      }
+    });
+
+    presetsPanel.add(backupfilesPresetsCombo);
+    return presetsPanel;
+  }
 
-  public JPanel initBackupsTabSuffixPanel()
+  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;
+
+    if (special)
+    {
+      if (key == 0)
+      {
+        // backupsSetCustomOptions();
+      }
+      else if (key == 255)
+      {
+        loadLastSavedBackupsOptions();
+        // switch to "Customise" scheme? probably not
+        // setComboIntStringKey(backupfilesPresetsCombo, 0);
+      }
+    }
+    else
+    {
+      if (backupfilesPresetEntriesValues.containsKey(key))
+      {
+        backupsSetOptions(backupfilesPresetEntriesValues.get(key));
+      }
+      else
+      {
+        System.out.println("Preset '" + value + "' not implemented");
+      }
+    }
+
+    backupfilesCustomOptionsSetEnabled();
+    updateBackupFilesExampleLabel();
+  }
+
+  protected int getComboIntStringKey(JComboBox c)
+  {
+    IntKeyStringValueEntry e = (IntKeyStringValueEntry) c.getSelectedItem();
+    return e != null ? e.getKey() : 0;
+  }
+
+  protected void setComboIntStringKey(JComboBox<IntKeyStringValueEntry> c,
+          int key)
+  {
+    for (int i = 0; i < c.getItemCount(); i++)
+    {
+      IntKeyStringValueEntry e = c.getItemAt(i);
+      int k = e.getKey();
+      String v = e.getValue();
+      if (e.getKey() == key)
+      {
+        c.setSelectedIndex(i);
+        break;
+      }
+    }
+    backupsTabUpdatePresets();
+  }
+
+  private JPanel initBackupsTabSuffixPanel()
   {
     suffixPanel.setBorder(new TitledBorder(
-            MessageManager.getString("label.suffix_format")));
+            MessageManager.getString("label.backup_filenames")));
     suffixPanel.setLayout(new GridBagLayout());
 
     suffixTemplateLabel
-            .setText(MessageManager.getString("label.suffix_template"));
+            .setText(MessageManager.getString("label.append_to_filename"));
     suffixTemplateLabel.setHorizontalAlignment(SwingConstants.LEFT);
     suffixTemplateLabel.setFont(LABEL_FONT);
 
-    final String tooltip = JvSwingUtils.wrapTooltip(true, MessageManager
-            .getString("label.suffix_template_tooltip"));
+    final String tooltip = JvSwingUtils.wrapTooltip(true,
+            MessageManager.getString("label.append_to_filename_tooltip"));
     suffixTemplate.setToolTipText(tooltip);
     suffixTemplate.addActionListener(new ActionListener()
     {
@@ -1779,6 +1931,7 @@ public class GPreferences extends JPanel
       public void actionPerformed(ActionEvent e)
       {
         updateBackupFilesExampleLabel();
+        backupfilesCustomOptionsSetEnabled();
       }
 
     });
@@ -1788,6 +1941,7 @@ public class GPreferences extends JPanel
       public void keyReleased(KeyEvent e)
       {
         updateBackupFilesExampleLabel();
+        backupfilesCustomOptionsSetEnabled();
       }
 
       @Override
@@ -1795,14 +1949,14 @@ public class GPreferences extends JPanel
       {
       }
 
-      // disable use of ':'
+      // disable use of ':' or '/' or '\'
       @Override
       public void keyTyped(KeyEvent e)
       {
         char c = e.getKeyChar();
-        if (c == ':')
+        if (c == ':' || c == '/' || c == '\\')
         {
-          // don't process ':'
+          // don't process ':' or '/' or '\'
           e.consume();
         }
       }
@@ -1828,8 +1982,7 @@ public class GPreferences extends JPanel
     };
     setIntegerSpinner(suffixDigitsSpinner, defaultmin, defaultmax, 3, c);
 
-    suffixReverse.setLabels(
-            MessageManager.getString("label.reverse_roll"),
+    suffixReverse.setLabels(MessageManager.getString("label.reverse_roll"),
             MessageManager.getString("label.increment_index"));
     suffixReverse.addActionListener(new ActionListener()
     {
@@ -1851,15 +2004,15 @@ public class GPreferences extends JPanel
     sgbc.weightx = 1.0;
     sgbc.weighty = 0.0;
     sgbc.fill = GridBagConstraints.NONE;
-    suffixPanel.add(suffixTemplateLabel,sgbc);
-    
+    suffixPanel.add(suffixTemplateLabel, sgbc);
+
     sgbc.gridx = 1;
     sgbc.fill = GridBagConstraints.HORIZONTAL;
     suffixPanel.add(suffixTemplate, sgbc);
-    
+
     // second row (number of digits spinner)
     sgbc.gridy = 1;
-    
+
     sgbc.gridx = 0;
     sgbc.fill = GridBagConstraints.NONE;
     suffixPanel.add(suffixDigitsLabel, sgbc);
@@ -1905,7 +2058,7 @@ public class GPreferences extends JPanel
       public void actionPerformed(ActionEvent e)
       {
         boolean selected = backupfilesKeepAll.isSelected();
-        keepRollMaxOptionsEnabled(!selected);
+        keepRollMaxOptionsEnabled();
         updateBackupFilesExampleLabel();
       }
     });
@@ -1925,7 +2078,7 @@ public class GPreferences extends JPanel
             MessageManager.getString("label.confirm_delete"),
             MessageManager.getString("label.auto_delete"));
     // update the enabled section
-    keepRollMaxOptionsEnabled(!backupfilesKeepAll.isSelected());
+    keepRollMaxOptionsEnabled();
 
     GridBagConstraints kgbc = new GridBagConstraints();
 
@@ -1970,37 +2123,44 @@ public class GPreferences extends JPanel
     JPanel jp = new JPanel();
     jp.setLayout(new FlowLayout());
     oldBackupFilesLabel
-            .setText(MessageManager.getString("label.old_backup_files"));
+            .setText(MessageManager
+                    .getString("label.autodelete_old_backup_files"));
     oldBackupFilesLabel.setFont(LABEL_FONT);
     oldBackupFilesLabel.setHorizontalAlignment(SwingConstants.LEFT);
     jp.add(oldBackupFilesLabel);
     jp.add(backupfilesConfirmDelete.getTrueButton());
     jp.add(backupfilesConfirmDelete.getFalseButton());
     keepfilesPanel.add(jp, kgbc);
-    
+
     return keepfilesPanel;
   }
 
   private JPanel initBackupsTabFilenameExamplesPanel()
   {
-    exampleFilesPanel.setBorder(new TitledBorder(
-            MessageManager.getString("label.example_filenames")));
+    String title = MessageManager
+            .getString("label.summary_of_backups_scheme");
+    TitledBorder tb = new TitledBorder(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(400, 85));
+    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.WEST;
-    gbc.fill = GridBagConstraints.NONE;
+    gbc.anchor = GridBagConstraints.EAST;
 
     gbc.gridy = 0;
     exampleFilesPanel.add(backupfilesExampleLabel, gbc);
@@ -2011,25 +2171,26 @@ public class GPreferences extends JPanel
   {
     int exampleindex = 12;
     String base = MessageManager.getString("label.filename");
-    if (base == null || base.length() > 0)
+    if (base == null || base.length() == 0)
     {
-      base = "filename";
+      base = "file_name.fa";
     }
-    String extension = ".fa";
 
+    boolean reverse = suffixReverse.isSelected();
+    boolean keepAll = backupfilesKeepAll.isSelected();
+    int rollMax = 4;
     String suffix = suffixTemplate.getText();
     int digits = 3;
-    try {
-      suffixDigitsSpinner.commitEdit();
-      digits = (Integer) suffixDigitsSpinner.getValue();
-      digits = digits < 1 ? 1 : digits;
-    } catch (Exception e)
+
+    backupfilesExampleLabel.setFont(LABEL_FONT_ITALIC);
+    if (suffix == null || suffix.length() == 0)
     {
-      System.out.println("Failed casting (Integer) suffixTemplateSpinner.getValue()");
+      backupfilesExampleLabel
+              .setText(MessageManager.getString("label.no_backup_files"));
+      backupfilesExampleLabel.setFont(LABEL_FONT_BOLD);
+      return;
     }
-    boolean reverse = suffixReverse.isSelected();
-    boolean keepAll = backupfilesKeepAll.isSelected();
-    int rollMax = 4;
+
     try
     {
       backupfilesRollMaxSpinner.commitEdit();
@@ -2041,6 +2202,22 @@ public class GPreferences extends JPanel
               "Failed casting (Integer) backupfilesRollMaxSpinner.getValue()");
     }
 
+    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()");
+    }
+
     int lowersurround = 2;
     int uppersurround = 0;
     StringBuilder exampleSB = new StringBuilder();
@@ -2071,7 +2248,7 @@ public class GPreferences extends JPanel
             exampleSB.append("\n");
           }
           exampleSB.append(BackupFilenameFilter.getBackupFilename(index,
-                  base, suffix, digits, extension));
+                  base, suffix, digits));
           if (min == max)
           {
             // no extra text needed
@@ -2081,7 +2258,7 @@ public class GPreferences extends JPanel
             String newest = MessageManager.getString("label.braced_newest");
             if (newest != null && newest.length() > 0)
             {
-              exampleSB.append(" "+newest);
+              exampleSB.append(" " + newest);
             }
           }
           else if (index == max)
@@ -2089,7 +2266,7 @@ public class GPreferences extends JPanel
             String oldest = MessageManager.getString("label.braced_oldest");
             if (oldest != null && oldest.length() > 0)
             {
-              exampleSB.append(" "+oldest);
+              exampleSB.append(" " + oldest);
             }
           }
         }
@@ -2124,7 +2301,7 @@ public class GPreferences extends JPanel
             exampleSB.append("\n");
           }
           exampleSB.append(BackupFilenameFilter.getBackupFilename(index,
-                  base, suffix, digits, extension));
+                  base, suffix, digits));
           if (min == max)
           {
             // no extra text needed
@@ -2134,7 +2311,7 @@ public class GPreferences extends JPanel
             String oldest = MessageManager.getString("label.braced_oldest");
             if (oldest != null && oldest.length() > 0)
             {
-              exampleSB.append(" "+oldest);
+              exampleSB.append(" " + oldest);
             }
           }
           else if (index == max)
@@ -2142,7 +2319,7 @@ public class GPreferences extends JPanel
             String newest = MessageManager.getString("label.braced_newest");
             if (newest != null && newest.length() > 0)
             {
-              exampleSB.append(" "+newest);
+              exampleSB.append(" " + newest);
             }
           }
         }
@@ -2187,7 +2364,7 @@ public class GPreferences extends JPanel
 
   }
 
-  public static int getSpinnerInt(JSpinner s, int def)
+  protected static int getSpinnerInt(JSpinner s, int def)
   {
     int i = def;
     try
@@ -2201,32 +2378,92 @@ public class GPreferences extends JPanel
     return i;
   }
 
-  private void keepRollMaxOptionsEnabled(boolean enabled)
+  private void keepRollMaxOptionsEnabled()
   {
+    boolean enabled = backupfilesKeepAll.isEnabled()
+            && !backupfilesKeepAll.isSelected();
     oldBackupFilesLabel.setEnabled(enabled);
     backupfilesRollMaxSpinner.setEnabled(enabled);
     backupfilesConfirmDelete.setEnabled(enabled);
-
   }
 
-  protected void backupsOptionsSetEnabled(boolean enabled)
+  private void backupfilesKeepAllSetEnabled(boolean tryEnabled)
   {
-    suffixPanel.setEnabled(enabled);
+    boolean enabled = tryEnabled && enableBackupFiles.isSelected()
+            && getComboIntStringKey(backupfilesPresetsCombo) == 0
+            && suffixTemplate.getText()
+                    .indexOf(BackupFiles.NUM_PLACEHOLDER) > -1;
     keepfilesPanel.setEnabled(enabled);
-    exampleFilesPanel.setEnabled(enabled);
+    backupfilesKeepAll.setEnabled(enabled);
+    oldBackupFilesLabel.setEnabled(enabled);
+    keepRollMaxOptionsEnabled();
+  }
 
-    suffixTemplate.setEnabled(enabled);
-    suffixTemplateLabel.setEnabled(enabled);
+  private void backupfilesSuffixTemplateDigitsSetEnabled()
+  {
+    boolean enabled = suffixTemplate.isEnabled() && suffixTemplate.getText()
+            .indexOf(BackupFiles.NUM_PLACEHOLDER) > -1;
     suffixDigitsLabel.setEnabled(enabled);
     suffixDigitsSpinner.setEnabled(enabled);
     suffixReverse.setEnabled(enabled);
-    backupfilesKeepAll.setEnabled(enabled);
-    backupfilesRollMaxSpinner.setEnabled(enabled);
-    oldBackupFilesLabel.setEnabled(enabled);
-    backupfilesConfirmDelete.setEnabled(enabled);
+  }
+
+  private void backupfilesSuffixTemplateSetEnabled(boolean tryEnabled)
+  {
+    boolean enabled = tryEnabled && enableBackupFiles.isSelected()
+            && getComboIntStringKey(backupfilesPresetsCombo) == 0;
+    suffixPanel.setEnabled(enabled);
+    suffixTemplateLabel.setEnabled(enabled);
+    suffixTemplate.setEnabled(enabled);
+    backupfilesSuffixTemplateDigitsSetEnabled();
+  }
+
+  protected void backupfilesCustomOptionsSetEnabled()
+  {
+    int scheme = getComboIntStringKey(backupfilesPresetsCombo);
+    boolean enabled = scheme == 0 && enableBackupFiles.isSelected();
+
+    backupfilesSuffixTemplateSetEnabled(enabled);
+    backupfilesKeepAllSetEnabled(enabled);
+  }
+
+  private void backupfilesSummarySetEnabled()
+  {
+    boolean enabled = enableBackupFiles.isSelected();
     backupfilesExampleLabel.setEnabled(enabled);
+    exampleFilesPanel.setEnabled(enabled);
+  }
+
+  private void backupfilesPresetsSetEnabled()
+  {
+    boolean enabled = enableBackupFiles.isSelected();
+    presetsPanel.setEnabled(enabled);
+    backupfilesPresetsCombo.setEnabled(enabled);
+  }
+
+  protected void backupsOptionsSetEnabled()
+  {
+    backupfilesPresetsSetEnabled();
+    backupfilesSummarySetEnabled();
+    backupfilesCustomOptionsSetEnabled();
+  }
+
+  protected void backupsSetOptions(String suffix, int digits,
+          boolean reverse, boolean keepAll, int rollMax,
+          boolean confirmDelete)
+  {
+    suffixTemplate.setText(suffix);
+    suffixDigitsSpinner.setValue(digits);
+    suffixReverse.setSelected(reverse);
+    backupfilesKeepAll.setSelected(keepAll);
+    backupfilesRollMaxSpinner.setValue(rollMax);
+    backupfilesConfirmDelete.setSelected(confirmDelete);
+  }
 
-    keepRollMaxOptionsEnabled(enabled && !backupfilesKeepAll.isSelected());
+  protected void backupsSetOptions(BackupFilesPresetEntry p)
+  {
+    backupsSetOptions(p.suffix, p.digits, p.reverse, p.keepAll, p.rollMax,
+            p.confirmDelete);
   }
 
   protected void autoIdWidth_actionPerformed()
@@ -2414,3 +2651,84 @@ 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