JAL-3048 JAL-3056 reinstate save/load project, colour scheme dialogs in
[jalview.git] / src / jalview / gui / UserDefinedColours.java
index 463d8cd..bee1b14 100755 (executable)
@@ -20,9 +20,7 @@
  */
 package jalview.gui;
 
-import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
-import jalview.datamodel.SequenceGroup;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
 import jalview.jbgui.GUserDefinedColours;
@@ -36,11 +34,11 @@ import jalview.schemes.UserColourScheme;
 import jalview.util.ColorUtils;
 import jalview.util.Format;
 import jalview.util.MessageManager;
+import jalview.util.dialogrunner.RunResponse;
 
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.Insets;
-import java.awt.event.ActionEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.io.File;
@@ -62,17 +60,17 @@ import javax.swing.event.ChangeListener;
  * @author Andrew Waterhouse
  * @author Mungo Carstairs
  */
-public class UserDefinedColours extends GUserDefinedColours implements
-        ChangeListener
+public class UserDefinedColours extends GUserDefinedColours
+        implements ChangeListener
 {
-  private static final Font VERDANA_BOLD_10 = new Font("Verdana",
-          Font.BOLD, 10);
+  private static final Font VERDANA_BOLD_10 = new Font("Verdana", Font.BOLD,
+          10);
 
   public static final String USER_DEFINED_COLOURS = "USER_DEFINED_COLOURS";
 
   private static final String LAST_DIRECTORY = "LAST_DIRECTORY";
 
-  private static final int MY_FRAME_HEIGHT = 420;
+  private static final int MY_FRAME_HEIGHT = 440;
 
   private static final int MY_FRAME_WIDTH = 810;
 
@@ -80,48 +78,44 @@ public class UserDefinedColours extends GUserDefinedColours implements
 
   AlignmentPanel ap;
 
-  SequenceGroup seqGroup;
-
-  List<JButton> selectedButtons;
-
+  /*
+   * the colour scheme when the dialog was opened, or
+   * the scheme last saved to file
+   */
   ColourSchemeI oldColourScheme;
 
-  JInternalFrame frame;
+  /*
+   * flag is true if the colour scheme has been changed since the
+   * dialog was opened, or the changes last saved to file
+   */
+  boolean changedButNotSaved;
 
-  JalviewStructureDisplayI structureViewer;
+  JInternalFrame frame;
 
   List<JButton> upperCaseButtons;
 
   List<JButton> lowerCaseButtons;
 
   /**
-   * Creates a new UserDefinedColours object.
+   * Creates and displays a new UserDefinedColours panel
    * 
-   * @param ap
-   * @param sg
+   * @param alignPanel
    */
-  public UserDefinedColours(AlignmentPanel ap, SequenceGroup sg)
+  public UserDefinedColours(AlignmentPanel alignPanel)
   {
     this();
 
     lcaseColour.setEnabled(false);
 
-    this.ap = ap;
-    seqGroup = sg;
+    this.ap = alignPanel;
 
-    if (seqGroup != null)
-    {
-      oldColourScheme = seqGroup.getColourScheme();
-    }
-    else
-    {
-      oldColourScheme = ap.av.getGlobalColourScheme();
-    }
+    oldColourScheme = alignPanel.av.getGlobalColourScheme();
 
     if (oldColourScheme instanceof UserColourScheme)
     {
       schemeName.setText(oldColourScheme.getSchemeName());
-      if (((UserColourScheme) oldColourScheme).getLowerCaseColours() != null)
+      if (((UserColourScheme) oldColourScheme)
+              .getLowerCaseColours() != null)
       {
         caseSensitive.setSelected(true);
         lcaseColour.setEnabled(true);
@@ -140,32 +134,10 @@ public class UserDefinedColours extends GUserDefinedColours implements
     showFrame();
   }
 
-  public UserDefinedColours(JalviewStructureDisplayI viewer,
-          ColourSchemeI oldcs)
-  {
-    this();
-    this.structureViewer = viewer;
-
-    colorChooser.getSelectionModel().addChangeListener(this);
-
-    oldColourScheme = oldcs;
-
-    if (oldColourScheme instanceof UserColourScheme)
-    {
-      schemeName.setText(((UserColourScheme) oldColourScheme)
-              .getSchemeName());
-    }
-
-    resetButtonPanel(false);
-
-    showFrame();
-
-  }
-
-  public UserDefinedColours()
+  UserDefinedColours()
   {
     super();
-    selectedButtons = new ArrayList<JButton>();
+    selectedButtons = new ArrayList<>();
   }
 
   void showFrame()
@@ -176,11 +148,6 @@ public class UserDefinedColours extends GUserDefinedColours implements
     Desktop.addInternalFrame(frame,
             MessageManager.getString("label.user_defined_colours"),
             MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
-
-    if (seqGroup != null)
-    {
-      frame.setTitle(frame.getTitle() + " (" + seqGroup.getName() + ")");
-    }
   }
 
   /**
@@ -197,7 +164,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
 
     if (upperCaseButtons == null)
     {
-      upperCaseButtons = new ArrayList<JButton>();
+      upperCaseButtons = new ArrayList<>();
     }
 
     for (int i = 0; i < 20; i++)
@@ -228,7 +195,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
 
       if (lowerCaseButtons == null)
       {
-        lowerCaseButtons = new ArrayList<JButton>();
+        lowerCaseButtons = new ArrayList<>();
       }
 
       for (int i = 0; i < 20; i++)
@@ -277,21 +244,25 @@ public class UserDefinedColours extends GUserDefinedColours implements
   {
     JButton button = null;
     final Color newColour = colorChooser.getColor();
-    for (int i = 0; i < selectedButtons.size(); i++)
-    {
-      button = selectedButtons.get(i);
-      button.setBackground(newColour);
-      button.setForeground(ColorUtils.brighterThan(newColour));
-    }
     if (lcaseColour.isSelected())
     {
+      selectedButtons.clear();
       for (int i = 0; i < lowerCaseButtons.size(); i++)
       {
         button = lowerCaseButtons.get(i);
         button.setBackground(newColour);
-        button.setForeground(ColorUtils.brighterThan(button.getBackground()));
+        button.setForeground(
+                ColorUtils.brighterThan(button.getBackground()));
       }
     }
+    for (int i = 0; i < selectedButtons.size(); i++)
+    {
+      button = selectedButtons.get(i);
+      button.setBackground(newColour);
+      button.setForeground(ColorUtils.brighterThan(newColour));
+    }
+
+    changedButNotSaved = true;
   }
 
   /**
@@ -353,8 +324,8 @@ public class UserDefinedColours extends GUserDefinedColours implements
         JButton button = (JButton) buttonPanel.getComponent(b);
         if (!selectedButtons.contains(button))
         {
-          button.setForeground(ColorUtils.brighterThan(button
-                  .getBackground()));
+          button.setForeground(
+                  ColorUtils.brighterThan(button.getBackground()));
           selectedButtons.add(button);
         }
       }
@@ -367,7 +338,8 @@ public class UserDefinedColours extends GUserDefinedColours implements
         button.setForeground(ColorUtils.darkerThan(button.getBackground()));
       }
       selectedButtons.clear();
-      pressed.setForeground(ColorUtils.brighterThan(pressed.getBackground()));
+      pressed.setForeground(
+              ColorUtils.brighterThan(pressed.getBackground()));
       selectedButtons.add(pressed);
 
     }
@@ -375,13 +347,14 @@ public class UserDefinedColours extends GUserDefinedColours implements
     {
       if (selectedButtons.contains(pressed))
       {
-        pressed.setForeground(ColorUtils.darkerThan(pressed.getBackground()));
+        pressed.setForeground(
+                ColorUtils.darkerThan(pressed.getBackground()));
         selectedButtons.remove(pressed);
       }
       else
       {
-        pressed.setForeground(ColorUtils.brighterThan(pressed
-                .getBackground()));
+        pressed.setForeground(
+                ColorUtils.brighterThan(pressed.getBackground()));
         selectedButtons.add(pressed);
       }
     }
@@ -472,15 +445,25 @@ public class UserDefinedColours extends GUserDefinedColours implements
   {
     if (isNoSelectionMade())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
-              .getString("label.no_colour_selection_in_scheme"),
+      JvOptionPane.showMessageDialog(Desktop.desktop,
+              MessageManager
+                      .getString("label.no_colour_selection_in_scheme"),
               MessageManager.getString("label.no_colour_selection_warn"),
               JvOptionPane.WARNING_MESSAGE);
     }
     else
     {
+      /*
+       * OK is treated as 'apply colours and close'
+       */
       applyButton_actionPerformed();
 
+      /*
+       * If editing a named colour scheme, warn if changes
+       * have not been saved
+       */
+      warnIfUnsavedChanges();
+
       try
       {
         frame.setClosed(true);
@@ -491,6 +474,59 @@ public class UserDefinedColours extends GUserDefinedColours implements
   }
 
   /**
+   * If we have made changes to an existing user defined colour scheme but not
+   * saved them, show a dialog with the option to save. If the user chooses to
+   * save, do so, else clear the colour scheme name to indicate a new colour
+   * scheme.
+   */
+  protected void warnIfUnsavedChanges()
+  {
+    // BH 2018 no warning in JavaScript TODO
+    
+    if (/** @j2sNative true || */ !changedButNotSaved)
+    {
+      return;
+    }
+
+    String name = schemeName.getText().trim();
+    if (oldColourScheme != null && !"".equals(name)
+            && name.equals(oldColourScheme.getSchemeName()))
+    {
+      String message = MessageManager.formatMessage("label.scheme_changed",
+              name);
+      String title = MessageManager.getString("label.save_changes");
+      String[] options = new String[] { title,
+          MessageManager.getString("label.dont_save_changes"), };
+      final String question = JvSwingUtils.wrapTooltip(true, message);
+      int response = JvOptionPane.showOptionDialog(Desktop.desktop,
+              question, title, JvOptionPane.DEFAULT_OPTION,
+              JvOptionPane.PLAIN_MESSAGE, null, options, options[0]);
+
+      if (response == 0)
+      {
+        /*
+         * prompt to save changes to file; if done,
+         * resets 'changed' flag to false
+         */
+        savebutton_actionPerformed();
+      }
+
+      /*
+       * if user chooses not to save (either in this dialog or in the
+       * save as dialogs), treat this as a new user defined colour scheme
+       */
+      if (changedButNotSaved)
+      {
+        /*
+         * clear scheme name and re-apply as an anonymous scheme
+         */
+        schemeName.setText("");
+        applyButton_actionPerformed();
+      }
+    }
+  }
+
+  /**
    * Returns true if the user has not made any colour selection (including if
    * 'case-sensitive' selected and no lower-case colour chosen).
    * 
@@ -508,37 +544,31 @@ public class UserDefinedColours extends GUserDefinedColours implements
   }
 
   /**
-   * Applies the current colour scheme to the alignment, sequence group or
-   * structure view.
+   * Applies the current colour scheme to the alignment or sequence group
    */
   @Override
   protected void applyButton_actionPerformed()
   {
     if (isNoSelectionMade())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
-              .getString("label.no_colour_selection_in_scheme"),
+      JvOptionPane.showMessageDialog(Desktop.desktop,
+              MessageManager
+                      .getString("label.no_colour_selection_in_scheme"),
               MessageManager.getString("label.no_colour_selection_warn"),
               JvOptionPane.WARNING_MESSAGE);
 
     }
     UserColourScheme ucs = getSchemeFromButtons();
 
-    if (seqGroup != null)
-    {
-      seqGroup.setColourScheme(ucs);
-      ap.paintAlignment(true);
-    }
-    else if (ap != null)
-    {
-      ap.alignFrame.changeColour(ucs);
-    }
-    else if (structureViewer != null)
-    {
-      structureViewer.setJalviewColourScheme(ucs);
-    }
+    ap.alignFrame.changeColour(ucs);
   }
 
+  /**
+   * Constructs an instance of UserColourScheme with the residue colours
+   * currently set on the buttons on the panel
+   * 
+   * @return
+   */
   UserColourScheme getSchemeFromButtons()
   {
 
@@ -590,72 +620,67 @@ public class UserDefinedColours extends GUserDefinedColours implements
       ucs.setLowerCaseColours(newColours);
     }
 
-    // if (ap != null)
-    // {
-    // ucs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
-    // }
-
     return ucs;
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Action on clicking Load scheme button.
+   * <ul>
+   * <li>Open a file chooser to browse for files with extension .jc</li>
+   * <li>Load in the colour scheme and transfer it to this panel's buttons</li>
+   * <li>Register the loaded colour scheme</li>
+   * </ul>
    */
   @Override
-  protected void loadbutton_actionPerformed(ActionEvent e)
+  protected void loadbutton_actionPerformed()
   {
-    upperCaseButtons = new ArrayList<JButton>();
-    lowerCaseButtons = new ArrayList<JButton>();
-
+    upperCaseButtons = new ArrayList<>();
+    lowerCaseButtons = new ArrayList<>();
     JalviewFileChooser chooser = new JalviewFileChooser("jc",
             "Jalview User Colours");
     chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle(MessageManager
-            .getString("label.load_colour_scheme"));
+    chooser.setDialogTitle(
+            MessageManager.getString("label.load_colour_scheme"));
     chooser.setToolTipText(MessageManager.getString("action.load"));
-
-    int value = chooser.showOpenDialog(this);
-
-    if (value != JalviewFileChooser.APPROVE_OPTION)
-    {
-      return;
-    }
-    File choice = chooser.getSelectedFile();
-    Cache.setProperty(LAST_DIRECTORY, choice.getParent());
-
-    UserColourScheme ucs = ColourSchemeLoader.loadColourScheme(choice
-            .getAbsolutePath());
-    Color[] colors = ucs.getColours();
-    schemeName.setText(ucs.getSchemeName());
-
-    if (ucs.getLowerCaseColours() != null)
-    {
-      caseSensitive.setSelected(true);
-      lcaseColour.setEnabled(true);
-      resetButtonPanel(true);
-      for (int i = 0; i < lowerCaseButtons.size(); i++)
-      {
-        JButton button = lowerCaseButtons.get(i);
-        button.setBackground(ucs.getLowerCaseColours()[i]);
-      }
-    }
-    else
-    {
-      caseSensitive.setSelected(false);
-      lcaseColour.setEnabled(false);
-      resetButtonPanel(false);
-    }
-
-    for (int i = 0; i < upperCaseButtons.size(); i++)
-    {
-      JButton button = upperCaseButtons.get(i);
-      button.setBackground(colors[i]);
-    }
-
-    addNewColourScheme(choice.getPath());
+    chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION) {
+               @Override
+               public void run() {
+                   File choice = chooser.getSelectedFile();
+                   Cache.setProperty(LAST_DIRECTORY, choice.getParent());
+
+                   UserColourScheme ucs = ColourSchemeLoader
+                           .loadColourScheme(choice.getAbsolutePath());
+                   Color[] colors = ucs.getColours();
+                   schemeName.setText(ucs.getSchemeName());
+
+                   if (ucs.getLowerCaseColours() != null)
+                   {
+                     caseSensitive.setSelected(true);
+                     lcaseColour.setEnabled(true);
+                     resetButtonPanel(true);
+                     for (int i = 0; i < lowerCaseButtons.size(); i++)
+                     {
+                       JButton button = lowerCaseButtons.get(i);
+                       button.setBackground(ucs.getLowerCaseColours()[i]);
+                     }
+                   }
+                   else
+                   {
+                     caseSensitive.setSelected(false);
+                     lcaseColour.setEnabled(false);
+                     resetButtonPanel(false);
+                   }
+
+                   for (int i = 0; i < upperCaseButtons.size(); i++)
+                   {
+                     JButton button = upperCaseButtons.get(i);
+                     button.setBackground(colors[i]);
+                   }
+
+                   addNewColourScheme(choice.getPath());
+               }});
+    
+    chooser.showOpenDialog(this);
   }
 
   /**
@@ -687,54 +712,74 @@ public class UserDefinedColours extends GUserDefinedColours implements
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Action on pressing the Save button.
+   * <ul>
+   * <li>Check a name has been entered</li>
+   * <li>Warn if the name already exists, remove any existing scheme of the same
+   * name if overwriting</li>
+   * <li>Do the standard file chooser thing to write with extension .jc</li>
+   * <li>If saving changes (possibly not yet applied) to the currently selected
+   * colour scheme, then apply the changes, as it is too late to back out
+   * now</li>
+   * <li>Don't apply the changes if the currently selected scheme is different,
+   * to allow a new scheme to be configured and saved but not applied</li>
+   * </ul>
+   * If the scheme is saved to file, the 'changed' flag field is reset to false.
    */
   @Override
-  protected void savebutton_actionPerformed(ActionEvent e)
+  protected void savebutton_actionPerformed()
   {
     String name = schemeName.getText().trim();
     if (name.length() < 1)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
-              .getString("label.user_colour_scheme_must_have_name"),
+      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+              MessageManager
+                      .getString("label.user_colour_scheme_must_have_name"),
               MessageManager.getString("label.no_name_colour_scheme"),
               JvOptionPane.WARNING_MESSAGE);
-      return;
     }
 
     if (ColourSchemes.getInstance().nameExists(name))
     {
-      int reply = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
+      // BH 2018 SwingJS bypasses this question with YES_OPTION
+      int reply = /** @j2sNative  0 &&  */ JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
               MessageManager.formatMessage(
-                      "label.colour_scheme_exists_overwrite", new Object[] {
-                          name, name }),
+                      "label.colour_scheme_exists_overwrite", new Object[]
+                      { name, name }),
               MessageManager.getString("label.duplicate_scheme_name"),
               JvOptionPane.YES_NO_OPTION);
       if (reply != JvOptionPane.YES_OPTION)
       {
-        return;
       }
-      ColourSchemes.getInstance().removeColourScheme(name);
     }
+
     JalviewFileChooser chooser = new JalviewFileChooser("jc",
             "Jalview User Colours");
 
     JalviewFileView fileView = new JalviewFileView();
     chooser.setFileView(fileView);
-    chooser.setDialogTitle(MessageManager
-            .getString("label.save_colour_scheme"));
+    chooser.setDialogTitle(
+            MessageManager.getString("label.save_colour_scheme"));
     chooser.setToolTipText(MessageManager.getString("action.save"));
-
-    int value = chooser.showSaveDialog(this);
-
-    if (value == JalviewFileChooser.APPROVE_OPTION)
-    {
-      File file = chooser.getSelectedFile();
-      addNewColourScheme(file.getPath());
-      saveToFile(file);
+    int option = chooser.showSaveDialog(this);
+    if (option == JalviewFileChooser.APPROVE_OPTION) 
+    {
+         File file = chooser.getSelectedFile();
+         UserColourScheme updatedScheme = addNewColourScheme(file.getPath());
+         saveToFile(file);
+         changedButNotSaved = false;
+
+         /*
+          * changes saved - apply to alignment if we are changing 
+          * the currently selected colour scheme; also make the updated
+          * colours the 'backout' scheme on Cancel
+          */
+         if (oldColourScheme != null
+                 && name.equals(oldColourScheme.getSchemeName()))
+         {
+           oldColourScheme = updatedScheme;
+           applyButton_actionPerformed();
+         }
     }
   }
 
@@ -745,15 +790,16 @@ public class UserDefinedColours extends GUserDefinedColours implements
    * the colour scheme.
    * 
    * @param filePath
+   * @return
    */
-  protected void addNewColourScheme(String filePath)
+  protected UserColourScheme addNewColourScheme(String filePath)
   {
     /*
      * update the delimited list of user defined colour files in
      * Jalview property USER_DEFINED_COLOURS
      */
-    String defaultColours = Cache
-            .getDefault(USER_DEFINED_COLOURS, filePath);
+    String defaultColours = Cache.getDefault(USER_DEFINED_COLOURS,
+            filePath);
     if (defaultColours.indexOf(filePath) == -1)
     {
       if (defaultColours.length() > 0)
@@ -777,6 +823,8 @@ public class UserDefinedColours extends GUserDefinedColours implements
     {
       ap.alignFrame.buildColourMenu();
     }
+
+    return ucs;
   }
 
   /**
@@ -791,7 +839,8 @@ public class UserDefinedColours extends GUserDefinedColours implements
      * marshal to file
      */
     JalviewUserColours ucs = new JalviewUserColours();
-    ucs.setSchemeName(schemeName.getText());
+    String name = schemeName.getText();
+    ucs.setSchemeName(name);
     try
     {
       PrintWriter out = new PrintWriter(new OutputStreamWriter(
@@ -814,30 +863,14 @@ public class UserDefinedColours extends GUserDefinedColours implements
   }
 
   /**
-   * On cancel, restores the colour scheme before the dialogue was opened
-   * 
-   * @param e
+   * On cancel, restores the colour scheme that was selected before the dialogue
+   * was opened
    */
   @Override
-  protected void cancelButton_actionPerformed(ActionEvent e)
+  protected void cancelButton_actionPerformed()
   {
-    if (ap != null)
-    {
-      if (seqGroup != null)
-      {
-        seqGroup.setColourScheme(oldColourScheme);
-      }
-      else
-      {
-        ap.alignFrame.changeColour(oldColourScheme);
-      }
-      ap.paintAlignment(true);
-    }
-
-    if (structureViewer != null)
-    {
-      structureViewer.setJalviewColourScheme(oldColourScheme);
-    }
+    ap.alignFrame.changeColour(oldColourScheme);
+    ap.paintAlignment(true, true);
 
     try
     {
@@ -847,8 +880,14 @@ public class UserDefinedColours extends GUserDefinedColours implements
     }
   }
 
+  /**
+   * Action on selecting or deselecting the Case Sensitive option. When
+   * selected, separate buttons are shown for lower case residues, and the panel
+   * is resized to accommodate them. Also, the checkbox for 'apply colour to all
+   * lower case' is enabled.
+   */
   @Override
-  public void caseSensitive_actionPerformed(ActionEvent e)
+  public void caseSensitive_actionPerformed()
   {
     boolean selected = caseSensitive.isSelected();
     resetButtonPanel(selected);