attNames = FeatureAttributes.getInstance()
+ .getAttributes(type);
+ textAttributeCombo = populateAttributesDropdown(type, attNames, false);
+ byTextPanel.add(textAttributeCombo);
+
+ /*
+ * disable colour by attribute if no attributes
+ */
+ if (attNames.isEmpty())
+ {
+ byAttributeText.setEnabled(false);
+ }
- private Color oldminColour;
+ return byTextPanel;
+ }
- public void minColour_actionPerformed()
+ /**
+ * Action on clicking the 'minimum colour' - open a colour chooser dialog, and
+ * set the selected colour (if the user does not cancel out of the dialog)
+ */
+ protected void minColour_actionPerformed()
{
Color col = JColorChooser.showDialog(this,
- "Select Colour for Minimum Value", minColour.getBackground());
+ MessageManager.getString("label.select_colour_minimum_value"),
+ minColour.getBackground());
if (col != null)
{
minColour.setBackground(col);
minColour.setForeground(col);
}
minColour.repaint();
- changeColour();
+ changeColour(true);
}
- public void maxColour_actionPerformed()
+ /**
+ * Action on clicking the 'maximum colour' - open a colour chooser dialog, and
+ * set the selected colour (if the user does not cancel out of the dialog)
+ */
+ protected void maxColour_actionPerformed()
{
Color col = JColorChooser.showDialog(this,
- "Select Colour for Maximum Value", maxColour.getBackground());
+ MessageManager.getString("label.select_colour_maximum_value"),
+ maxColour.getBackground());
if (col != null)
{
maxColour.setBackground(col);
maxColour.setForeground(col);
}
maxColour.repaint();
- changeColour();
+ changeColour(true);
}
- void changeColour()
+ /**
+ * Constructs and sets the selected colour options as the colour for the
+ * feature type, and repaints the alignment, and optionally the Overview
+ * and/or structure viewer if open
+ *
+ * @param updateStructsAndOverview
+ */
+ void changeColour(boolean updateStructsAndOverview)
{
// Check if combobox is still adjusting
if (adjusting)
@@ -358,107 +704,142 @@ public class FeatureColourChooser extends JalviewDialog
return;
}
- int aboveThreshold = AnnotationColourGradient.NO_THRESHOLD;
- if (threshold.getSelectedItem().equals("Above Threshold"))
+ boolean aboveThreshold = false;
+ boolean belowThreshold = false;
+ if (threshold.getSelectedIndex() == 1)
{
- aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
+ aboveThreshold = true;
}
- else if (threshold.getSelectedItem().equals("Below Threshold"))
+ else if (threshold.getSelectedIndex() == 2)
{
- aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
+ belowThreshold = true;
}
+ boolean hasThreshold = aboveThreshold || belowThreshold;
slider.setEnabled(true);
thresholdValue.setEnabled(true);
- GraduatedColor acg;
+ /*
+ * make the feature colour
+ */
+ FeatureColourI acg;
if (cs.isColourByLabel())
{
- acg = new GraduatedColor(oldminColour, oldmaxColour, min, max);
+ acg = new FeatureColour(oldminColour, oldmaxColour, min, max);
}
else
{
- acg = new GraduatedColor(oldminColour = minColour.getBackground(),
- oldmaxColour = maxColour.getBackground(), min, max);
-
+ acg = new FeatureColour(oldminColour = minColour.getBackground(),
+ oldmaxColour = maxColour.getBackground(),
+ oldNoColour = noColour, min, max);
+ }
+ String attribute = null;
+ textAttributeCombo.setEnabled(false);
+ valueAttributeCombo.setEnabled(false);
+ if (byAttributeText.isSelected())
+ {
+ attribute = (String) textAttributeCombo.getSelectedItem();
+ textAttributeCombo.setEnabled(true);
+ acg.setAttributeName(attribute.split(COLON));
+ }
+ else if (byAttributeValue.isSelected())
+ {
+ attribute = (String) valueAttributeCombo.getSelectedItem();
+ valueAttributeCombo.setEnabled(true);
+ acg.setAttributeName(attribute.split(COLON));
+ }
+ else
+ {
+ acg.setAttributeName((String) null);
}
- if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
+ if (!hasThreshold)
{
slider.setEnabled(false);
thresholdValue.setEnabled(false);
thresholdValue.setText("");
thresholdIsMin.setEnabled(false);
}
- else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
- && threshline == null)
+ else if (threshline == null)
{
- // todo visual indication of feature threshold
- threshline = new jalview.datamodel.GraphLine((max - min) / 2f,
- "Threshold", Color.black);
+ /*
+ * todo not yet implemented: visual indication of feature threshold
+ */
+ threshline = new GraphLine((max - min) / 2f, "Threshold",
+ Color.black);
}
- if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
+ if (hasThreshold)
{
adjusting = true;
- acg.setThresh(threshline.value);
+ acg.setThreshold(threshline.value);
- float range = max * 1000f - min * 1000f;
+ float range = (max - min) * scaleFactor;
- slider.setMinimum((int) (min * 1000));
- slider.setMaximum((int) (max * 1000));
- slider.setValue((int) (threshline.value * 1000));
+ slider.setMinimum((int) (min * scaleFactor));
+ slider.setMaximum((int) (max * scaleFactor));
+ // slider.setValue((int) (threshline.value * scaleFactor));
+ slider.setValue(Math.round(threshline.value * scaleFactor));
thresholdValue.setText(threshline.value + "");
slider.setMajorTickSpacing((int) (range / 10f));
slider.setEnabled(true);
thresholdValue.setEnabled(true);
- thresholdIsMin.setEnabled(!colourByLabel.isSelected());
+ thresholdIsMin.setEnabled(!byDescription.isSelected());
adjusting = false;
}
- acg.setThreshType(aboveThreshold);
- if (thresholdIsMin.isSelected()
- && aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
+ acg.setAboveThreshold(aboveThreshold);
+ acg.setBelowThreshold(belowThreshold);
+ if (thresholdIsMin.isSelected() && hasThreshold)
{
acg.setAutoScaled(false);
- if (aboveThreshold == AnnotationColourGradient.ABOVE_THRESHOLD)
+ if (aboveThreshold)
{
- acg = new GraduatedColor(acg, threshline.value, max);
+ acg = new FeatureColour((FeatureColour) acg, threshline.value, max);
}
else
{
- acg = new GraduatedColor(acg, min, threshline.value);
+ acg = new FeatureColour((FeatureColour) acg, min, threshline.value);
}
}
else
{
acg.setAutoScaled(true);
}
- acg.setColourByLabel(colourByLabel.isSelected());
+ acg.setColourByLabel(byDescription.isSelected()
+ || byAttributeText.isSelected());
+
if (acg.isColourByLabel())
{
maxColour.setEnabled(false);
minColour.setEnabled(false);
+ noValueCombo.setEnabled(false);
maxColour.setBackground(this.getBackground());
maxColour.setForeground(this.getBackground());
minColour.setBackground(this.getBackground());
minColour.setForeground(this.getBackground());
-
}
else
{
maxColour.setEnabled(true);
minColour.setEnabled(true);
+ noValueCombo.setEnabled(true);
maxColour.setBackground(oldmaxColour);
- minColour.setBackground(oldminColour);
maxColour.setForeground(oldmaxColour);
+ minColour.setBackground(oldminColour);
minColour.setForeground(oldminColour);
+ noColour = oldNoColour;
}
- fr.featureColours.put(type, acg);
+
+ /*
+ * save the colour, and repaint stuff
+ */
+ fr.setColour(type, acg);
cs = acg;
- ap.paintAlignment(false);
+ ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
}
+ @Override
protected void raiseClosed()
{
if (this.colourEditor != null)
@@ -467,79 +848,159 @@ public class FeatureColourChooser extends JalviewDialog
}
}
+ @Override
public void okPressed()
{
- changeColour();
+ changeColour(false);
}
-
+ @Override
public void cancelPressed()
{
reset();
}
+ /**
+ * Action when the user cancels the dialog. All previous settings should be
+ * restored and rendered on the alignment, and any linked Overview window or
+ * structure.
+ */
void reset()
{
- fr.featureColours.put(type, oldcs);
- ap.paintAlignment(false);
+ fr.setColour(type, oldcs);
+ ap.paintAlignment(true, true);
cs = null;
}
- public void thresholdCheck_actionPerformed(ActionEvent e)
- {
- changeColour();
- }
-
- public void annotations_actionPerformed(ActionEvent e)
- {
- changeColour();
- }
-
- public void threshold_actionPerformed(ActionEvent e)
- {
- changeColour();
- }
-
- public void thresholdValue_actionPerformed(ActionEvent e)
+ /**
+ * Action on text entry of a threshold value
+ */
+ protected void thresholdValue_actionPerformed()
{
try
{
float f = Float.parseFloat(thresholdValue.getText());
- slider.setValue((int) (f * 1000));
+ slider.setValue((int) (f * scaleFactor));
threshline.value = f;
+
+ /*
+ * force repaint of any Overview window or structure
+ */
+ ap.paintAlignment(true, true);
} catch (NumberFormatException ex)
{
}
}
- public void valueChanged()
+ /**
+ * Action on change of threshold slider value. This may be done interactively
+ * (by moving the slider), or programmatically (to update the slider after
+ * manual input of a threshold value).
+ */
+ protected void sliderValueChanged()
{
- threshline.value = (float) slider.getValue() / 1000f;
- cs.setThresh(threshline.value);
- changeColour();
- ap.paintAlignment(false);
+ /*
+ * squash rounding errors by forcing min/max of slider to
+ * actual min/max of feature score range
+ */
+ int value = slider.getValue();
+ threshline.value = value == slider.getMaximum() ? max
+ : (value == slider.getMinimum() ? min : value / scaleFactor);
+ cs.setThreshold(threshline.value);
+
+ /*
+ * repaint alignment, but not Overview or structure,
+ * to avoid overload while dragging the slider
+ */
+ changeColour(false);
}
- public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
+ void addActionListener(ActionListener graduatedColorEditor)
{
- changeColour();
+ if (colourEditor != null)
+ {
+ System.err.println(
+ "IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
+ }
+ colourEditor = graduatedColorEditor;
}
- public void colourByLabel_actionPerformed(ActionEvent actionEvent)
+ /**
+ * Answers the last colour setting selected by user - either oldcs (which may
+ * be a java.awt.Color) or the new GraduatedColor
+ *
+ * @return
+ */
+ FeatureColourI getLastColour()
{
- changeColour();
+ if (cs == null)
+ {
+ return oldcs;
+ }
+ return cs;
}
- ActionListener colourEditor = null;
-
- public void addActionListener(ActionListener graduatedColorEditor)
+ /**
+ * A helper method to build the drop-down choice of attributes for a feature.
+ * Where metadata is available with a description for an attribute, that is
+ * added as a tooltip. The list may optionally be restricted to attributes for
+ * which we hold a range of numerical values (so suitable candidates for a
+ * graduated colour scheme).
+ *
+ * Attribute names may be 'simple' e.g. "AC" or 'compound' e.g. {"CSQ",
+ * "Allele"}. Compound names are rendered for display as (e.g.) CSQ:Allele.
+ *
+ * @param featureType
+ * @param attNames
+ * @param withNumericRange
+ */
+ protected JComboBox populateAttributesDropdown(
+ String featureType, List attNames,
+ boolean withNumericRange)
{
- if (colourEditor != null)
+ List validAtts = new ArrayList<>();
+ List tooltips = new ArrayList<>();
+
+ FeatureAttributes fa = FeatureAttributes.getInstance();
+ for (String[] attName : attNames)
+ {
+ if (withNumericRange)
+ {
+ float[] minMax = fa.getMinMax(featureType, attName);
+ if (minMax == null)
+ {
+ continue;
+ }
+ }
+ validAtts.add(String.join(COLON, attName));
+ String desc = fa.getDescription(featureType, attName);
+ if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
+ {
+ desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
+ }
+ tooltips.add(desc == null ? "" : desc);
+ }
+
+ JComboBox attCombo = JvSwingUtils.buildComboWithTooltips(
+ validAtts, tooltips);
+
+ attCombo.addItemListener(new ItemListener()
{
- System.err
- .println("IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ changeMinMaxAction.actionPerformed(null);
+ }
+ });
+
+ if (validAtts.isEmpty())
+ {
+ attCombo.setToolTipText(MessageManager
+ .getString(withNumericRange ? "label.no_numeric_attributes"
+ : "label.no_attributes"));
}
- colourEditor = graduatedColorEditor;
+
+ return attCombo;
}
}