Merge branch 'develop' into features/JAL-3010ontologyFeatureSettings
[jalview.git] / src / jalview / gui / FeatureTypeSettings.java
index 7aef644..0dd0f1f 100644 (file)
@@ -30,6 +30,7 @@ import jalview.datamodel.features.FeatureMatcherI;
 import jalview.datamodel.features.FeatureMatcherSet;
 import jalview.datamodel.features.FeatureMatcherSetI;
 import jalview.io.gff.SequenceOntologyFactory;
+import jalview.io.gff.SequenceOntologyI;
 import jalview.schemes.FeatureColour;
 import jalview.util.ColorUtils;
 import jalview.util.MessageManager;
@@ -213,14 +214,20 @@ public class FeatureTypeSettings extends JalviewDialog
   private JPanel chooseFiltersPanel;
 
   /*
-   * feature types present in Feature Renderer which are
-   * sub-types of the one this editor is acting on
+   * the root Sequence Ontology terms (if any) that is a parent of
+   * the current feature type
    */
-  private final List<String> subTypes;
+  private String rootSOTerm;
+
+  /*
+   * feature types present in Feature Renderer which have the same Sequence
+   * Ontology root parent as the one this editor is acting on
+   */
+  private final List<String> peerSoTerms;
 
   /*
    * if true, filter or colour settings are also applied to 
-   * any feature sub-types in the Sequence Ontology
+   * any sub-types of rootSOTerm in the Sequence Ontology
    */
   private boolean applyFiltersToSubtypes;
 
@@ -234,31 +241,11 @@ public class FeatureTypeSettings extends JalviewDialog
    */
   public FeatureTypeSettings(FeatureRenderer frender, String theType)
   {
-    this(frender, false, theType);
-  }
-
-  /**
-   * Constructor, with option to make a blocking dialog (has to complete in the
-   * AWT event queue thread). Currently this option is always set to false.
-   * 
-   * @param frender
-   * @param blocking
-   * @param theType
-   */
-  FeatureTypeSettings(FeatureRenderer frender, boolean blocking,
-          String theType)
-  {
     this.fr = frender;
     this.featureType = theType;
     ap = fr.ap;
 
-    /*
-     * determine sub-types (if any) of this feature type
-     */
-    List<String> types = fr.getRenderOrder();
-    subTypes = SequenceOntologyFactory.getInstance()
-            .getChildTerms(this.featureType, types);
-    Collections.sort(subTypes); // sort for ease of reading in tooltip
+    peerSoTerms = findSequenceOntologyPeers(this.featureType);
 
     /*
      * save original colours and filters for this feature type
@@ -268,7 +255,7 @@ public class FeatureTypeSettings extends JalviewDialog
     originalFilters.put(theType, fr.getFeatureFilter(theType));
     originalColours = new HashMap<>();
     originalColours.put(theType, fr.getFeatureColours().get(theType));
-    for (String child : subTypes)
+    for (String child : peerSoTerms)
     {
       originalFilters.put(child, fr.getFeatureFilter(child));
       originalColours.put(child, fr.getFeatureColours().get(child));
@@ -296,12 +283,50 @@ public class FeatureTypeSettings extends JalviewDialog
     String title = MessageManager
             .formatMessage("label.display_settings_for", new String[]
             { theType });
-    initDialogFrame(this, true, blocking, title, 500, 500);
-
+    initDialogFrame(this, true, false, title, 580, 500);
     waitForInput();
   }
 
   /**
+   * Answers a (possibly empty) list of feature types known to the Feature
+   * Renderer which share a top level Sequence Ontology parent with the current
+   * feature type. The current type is not included.
+   * 
+   * @return
+   */
+  protected List<String> findSequenceOntologyPeers(String featureType)
+  {
+    List<String> peers = new ArrayList<>();
+
+    /*
+     * first find the SO term (if any) that is the root
+     * parent of the current type
+     */
+    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    List<String> roots = so.getRootParents(featureType);
+    if (roots == null || roots.size() > 1)
+    {
+      /*
+       * feature type is not an SO term, or has ambiguous root
+       */
+      return peers;
+    }
+    rootSOTerm = roots.get(0);
+
+    List<String> types = fr.getRenderOrder();
+    for (String type : types)
+    {
+      if (!type.equals(featureType) && so.isA(type, rootSOTerm))
+      {
+        peers.add(type);
+      }
+
+    }
+    Collections.sort(peers); // sort for ease of reading in tooltip
+    return peers;
+  }
+
+  /**
    * Configures the widgets on the Colours tab according to the current feature
    * colour scheme
    */
@@ -320,9 +345,9 @@ public class FeatureTypeSettings extends JalviewDialog
        */
       if (fc.isSimpleColour())
       {
-        simpleColour.setSelected(true);
         singleColour.setBackground(fc.getColour());
         singleColour.setForeground(fc.getColour());
+        simpleColour.setSelected(true);
       }
 
       /*
@@ -420,7 +445,7 @@ public class FeatureTypeSettings extends JalviewDialog
                         : BELOW_THRESHOLD_OPTION);
         slider.setEnabled(true);
         slider.setValue((int) (fc.getThreshold() * scaleFactor));
-        thresholdValue.setText(String.valueOf(getRoundedSliderValue()));
+        thresholdValue.setText(String.valueOf(fc.getThreshold()));
         thresholdValue.setEnabled(true);
         thresholdIsMin.setEnabled(true);
       }
@@ -617,14 +642,21 @@ public class FeatureTypeSettings extends JalviewDialog
     maxColour.setBorder(new LineBorder(Color.black));
 
     /*
-     * default max colour to current colour (if a plain colour),
-     * or to Black if colour by label;  make min colour a pale
-     * version of max colour
+     * if not set, default max colour to last plain colour,
+     * and make min colour a pale version of max colour
      */
-    FeatureColourI fc = fr.getFeatureColours().get(featureType);
-    Color bg = fc.isSimpleColour() ? fc.getColour() : Color.BLACK;
-    maxColour.setBackground(bg);
-    minColour.setBackground(ColorUtils.bleachColour(bg, 0.9f));
+    FeatureColourI originalColour = originalColours.get(featureType);
+    Color max = originalColour.getMaxColour();
+    if (max == null)
+    {
+      max = originalColour.getColour();
+      minColour.setBackground(ColorUtils.bleachColour(max, 0.9f));
+    }
+    else
+    {
+      maxColour.setBackground(max);
+      minColour.setBackground(originalColour.getMinColour());
+    }
 
     noValueCombo = new JComboBox<>();
     noValueCombo.addItem(MessageManager.getString("label.no_colour"));
@@ -707,6 +739,7 @@ public class FeatureTypeSettings extends JalviewDialog
         {
           thresholdValue
                   .setText(String.valueOf(slider.getValue() / scaleFactor));
+          thresholdValue.setBackground(Color.white); // to reset red for invalid
           sliderValueChanged();
         }
       }
@@ -778,9 +811,9 @@ public class FeatureTypeSettings extends JalviewDialog
             MessageManager.getString("action.colour"), true);
 
     /*
-     * option to apply colour to sub-types as well (if there are any)
+     * option to apply colour to peer types as well (if there are any)
      */
-    if (!subTypes.isEmpty())
+    if (!peerSoTerms.isEmpty())
     {
       applyColourToSubtypes = false;
       colourByPanel.add(initSubtypesPanel(false));
@@ -791,8 +824,6 @@ public class FeatureTypeSettings extends JalviewDialog
      */
     JPanel simpleColourPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     simpleColourPanel.setBackground(Color.white);
-//    JvSwingUtils.createTitledBorder(simpleColourPanel,
-//            MessageManager.getString("label.simple"), true);
     colourByPanel.add(simpleColourPanel);
 
     simpleColour = new JRadioButton(
@@ -805,15 +836,18 @@ public class FeatureTypeSettings extends JalviewDialog
       {
         if (simpleColour.isSelected() && !adjusting)
         {
-          showColourChooser(singleColour, "label.select_colour");
+          colourChanged(true);
         }
       }
-
     });
 
     singleColour.setFont(JvSwingUtils.getLabelFont());
     singleColour.setBorder(BorderFactory.createLineBorder(Color.black));
     singleColour.setPreferredSize(new Dimension(40, 20));
+    FeatureColourI originalColour = originalColours.get(featureType);
+    singleColour.setBackground(originalColour.getColour());
+    singleColour.setForeground(originalColour.getColour());
+
     singleColour.addMouseListener(new MouseAdapter()
     {
       @Override
@@ -894,7 +928,7 @@ public class FeatureTypeSettings extends JalviewDialog
     JPanel toSubtypes = new JPanel(new FlowLayout(FlowLayout.LEFT));
     toSubtypes.setBackground(Color.WHITE);
     JCheckBox applyToSubtypesCB = new JCheckBox(MessageManager
-            .formatMessage("label.apply_to_subtypes", featureType));
+            .formatMessage("label.apply_to_subtypes", rootSOTerm));
     applyToSubtypesCB.setToolTipText(getSubtypesTooltip());
     applyToSubtypesCB.addActionListener(new ActionListener()
     {
@@ -967,7 +1001,7 @@ public class FeatureTypeSettings extends JalviewDialog
     fr.setColour(featureType, acg);
     if (applyColourToSubtypes)
     {
-      for (String child : subTypes)
+      for (String child : peerSoTerms)
       {
         fr.setColour(child, acg);
       }
@@ -986,42 +1020,9 @@ public class FeatureTypeSettings extends JalviewDialog
   private FeatureColourI makeColourFromInputs()
   {
     /*
-     * easiest case - a single colour
-     */
-    if (simpleColour.isSelected())
-    {
-      return new FeatureColour(singleColour.getBackground());
-    }
-
-    /*
-     * next easiest case - colour by Label, or attribute text
-     */
-    if (byCategory.isSelected())
-    {
-      Color c = this.getBackground();
-      FeatureColourI fc = new FeatureColour(c, c, null, 0f, 0f);
-      fc.setColourByLabel(true);
-      String byWhat = (String) colourByTextCombo.getSelectedItem();
-      if (!LABEL_18N.equals(byWhat))
-      {
-        fc.setAttributeName(
-                FeatureMatcher.fromAttributeDisplayName(byWhat));
-      }
-      return fc;
-    }
-
-    /*
-     * remaining case - graduated colour by score, or attribute value
+     * min-max range is to (or from) threshold value if 
+     * 'threshold is min/max' is selected 
      */
-    Color noColour = null;
-    if (noValueCombo.getSelectedIndex() == MIN_COLOUR_OPTION)
-    {
-      noColour = minColour.getBackground();
-    }
-    else if (noValueCombo.getSelectedIndex() == MAX_COLOUR_OPTION)
-    {
-      noColour = maxColour.getBackground();
-    }
 
     float thresh = 0f;
     try
@@ -1031,11 +1032,6 @@ public class FeatureTypeSettings extends JalviewDialog
     {
       // invalid inputs are already handled on entry
     }
-
-    /*
-     * min-max range is to (or from) threshold value if 
-     * 'threshold is min/max' is selected 
-     */
     float minValue = min;
     float maxValue = max;
     final int thresholdOption = threshold.getSelectedIndex();
@@ -1049,14 +1045,50 @@ public class FeatureTypeSettings extends JalviewDialog
     {
       maxValue = thresh;
     }
+    Color noColour = null;
+    if (noValueCombo.getSelectedIndex() == MIN_COLOUR_OPTION)
+    {
+      noColour = minColour.getBackground();
+    }
+    else if (noValueCombo.getSelectedIndex() == MAX_COLOUR_OPTION)
+    {
+      noColour = maxColour.getBackground();
+    }
 
     /*
-     * make the graduated colour
+     * construct a colour that 'remembers' all the options, including
+     * those not currently selected
      */
-    FeatureColourI fc = new FeatureColour(minColour.getBackground(),
-            maxColour.getBackground(), noColour, minValue, maxValue);
+    FeatureColourI fc = new FeatureColour(singleColour.getBackground(),
+            minColour.getBackground(), maxColour.getBackground(), noColour,
+            minValue, maxValue);
 
     /*
+     * easiest case - a single colour
+     */
+    if (simpleColour.isSelected())
+    {
+      ((FeatureColour) fc).setGraduatedColour(false);
+      return fc;
+    }
+
+    /*
+     * next easiest case - colour by Label, or attribute text
+     */
+    if (byCategory.isSelected())
+    {
+      fc.setColourByLabel(true);
+      String byWhat = (String) colourByTextCombo.getSelectedItem();
+      if (!LABEL_18N.equals(byWhat))
+      {
+        fc.setAttributeName(
+                FeatureMatcher.fromAttributeDisplayName(byWhat));
+      }
+      return fc;
+    }
+
+    /*
+     * remaining case - graduated colour by score, or attribute value;
      * set attribute to colour by if selected
      */
     String byWhat = (String) colourByRangeCombo.getSelectedItem();
@@ -1147,21 +1179,23 @@ public class FeatureTypeSettings extends JalviewDialog
   {
     try
     {
+      /*
+       * set 'adjusting' flag while moving the slider, so it 
+       * doesn't then in turn change the value (with rounding)
+       */
       adjusting = true;
       float f = Float.parseFloat(thresholdValue.getText());
+      f = Float.max(f, this.min);
+      f = Float.min(f, this.max);
+      thresholdValue.setText(String.valueOf(f));
       slider.setValue((int) (f * scaleFactor));
       threshline.value = f;
       thresholdValue.setBackground(Color.white); // ok
-
-      /*
-       * force repaint of any Overview window or structure
-       */
-      ap.paintAlignment(true, true);
+      adjusting = false;
+      colourChanged(true);
     } catch (NumberFormatException ex)
     {
       thresholdValue.setBackground(Color.red); // not ok
-    } finally
-    {
       adjusting = false;
     }
   }
@@ -1283,9 +1317,9 @@ public class FeatureTypeSettings extends JalviewDialog
     outerPanel.setBackground(Color.white);
 
     /*
-     * option to apply colour to sub-types as well (if there are any)
+     * option to apply colour to peer types as well (if there are any)
      */
-    if (!subTypes.isEmpty())
+    if (!peerSoTerms.isEmpty())
     {
       applyFiltersToSubtypes = false;
       outerPanel.add(initSubtypesPanel(true));
@@ -1321,13 +1355,7 @@ public class FeatureTypeSettings extends JalviewDialog
   {
     JPanel andOrPanel = new JPanel(new BorderLayout());
     andOrPanel.setBackground(Color.white);
-//<<<<<<< HEAD
-//    JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
-//    andOrPanel.add(panel1, BorderLayout.WEST);
-//    panel1.setBackground(Color.white);
-//    panel1.setBorder(BorderFactory.createLineBorder(debugBorderColour));
-//=======
-//>>>>>>> refs/heads/develop
+
     andFilters = new JRadioButton(MessageManager.getString("label.and"));
     orFilters = new JRadioButton(MessageManager.getString("label.or"));
     ActionListener actionListener = new ActionListener()
@@ -1360,9 +1388,9 @@ public class FeatureTypeSettings extends JalviewDialog
    */
   protected String getSubtypesTooltip()
   {
-    StringBuilder sb = new StringBuilder(20 * subTypes.size());
+    StringBuilder sb = new StringBuilder(20 * peerSoTerms.size());
     sb.append(MessageManager.getString("label.apply_also_to"));
-    for (String child : subTypes)
+    for (String child : peerSoTerms)
     {
       sb.append("<br>").append(child);
     }
@@ -1906,7 +1934,7 @@ public class FeatureTypeSettings extends JalviewDialog
     fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
     if (applyFiltersToSubtypes)
     {
-      for (String child : subTypes)
+      for (String child : peerSoTerms)
       {
         fr.setFeatureFilter(child, combined.isEmpty() ? null : combined);
       }