*/
package jalview.gui;
+import jalview.api.AlignViewportI;
import jalview.api.AlignmentViewPanel;
import jalview.api.FeatureColourI;
+import jalview.bin.Console;
import jalview.datamodel.GraphLine;
import jalview.datamodel.features.FeatureAttributes;
import jalview.datamodel.features.FeatureAttributes.Datatype;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.math.BigDecimal;
+import java.math.MathContext;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
-import javax.swing.JSlider;
import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
*/
public class FeatureTypeSettings extends JalviewDialog
{
+ private final static MathContext FOUR_SIG_FIG = new MathContext(4);
+
private final static String LABEL_18N = MessageManager
.getString("label.label");
private float max;
/*
- * scale factor for conversion between absolute min-max and slider
- */
- float scaleFactor;
-
- /*
* radio button group, to select what to colour by:
* simple colour, by category (text), or graduated
*/
JRadioButton graduatedColour = new JRadioButton();
JPanel coloursPanel;
-
+
JPanel filtersPanel;
JPanel singleColour = new JPanel();
JPanel maxColour = new JPanel();
- private JComboBox<String> threshold = new JComboBox<>();
+ private JComboBox<Object> threshold = new JComboBox<>();
- JSlider slider = new JSlider();
+ private Slider slider;
JTextField thresholdValue = new JTextField(20);
/*
* choice of option for 'colour for no value'
*/
- private JComboBox<String> noValueCombo;
+ private JComboBox<Object> noValueCombo;
/*
* choice of what to colour by text (Label or attribute)
*/
- private JComboBox<String> colourByTextCombo;
+ private JComboBox<Object> colourByTextCombo;
/*
* choice of what to colour by range (Score or attribute)
*/
- private JComboBox<String> colourByRangeCombo;
+ private JComboBox<Object> colourByRangeCombo;
private JRadioButton andFilters;
ap = fr.ap;
originalFilter = fr.getFeatureFilter(theType);
originalColour = fr.getFeatureColours().get(theType);
-
+
adjusting = true;
-
+
try
{
initialise();
ex.printStackTrace();
return;
}
-
+
updateColoursPanel();
-
+
updateFiltersPanel();
-
+
adjusting = false;
-
+
colourChanged(false);
-
+
String title = MessageManager
.formatMessage("label.display_settings_for", new String[]
{ theType });
}
/**
- * Configures the widgets on the Colours panel according to the current feature
- * colour scheme
+ * Configures the widgets on the Colours panel according to the current
+ * feature colour scheme
*/
private void updateColoursPanel()
{
* update min-max scaling if there is a range to work with,
* else disable the widgets (this shouldn't happen if only
* valid options are offered in the combo box)
+ * offset slider to have only non-negative values if necessary (JAL-2983)
*/
- scaleFactor = (max == min) ? 1f : 100f / (max - min);
- float range = (max - min) * scaleFactor;
- slider.setMinimum((int) (min * scaleFactor));
- slider.setMaximum((int) (max * scaleFactor));
- slider.setMajorTickSpacing((int) (range / 10f));
+ slider.setSliderModel(min, max, min);
+ slider.setMajorTickSpacing(
+ (int) ((slider.getMaximum() - slider.getMinimum()) / 10f));
threshline = new GraphLine((max - min) / 2f, "Threshold",
Color.black);
fc.isAboveThreshold() ? ABOVE_THRESHOLD_OPTION
: BELOW_THRESHOLD_OPTION);
slider.setEnabled(true);
- slider.setValue((int) (fc.getThreshold() * scaleFactor));
- thresholdValue.setText(String.valueOf(getRoundedSliderValue()));
+ slider.setSliderValue(fc.getThreshold());
+ setThresholdValueText(fc.getThreshold());
thresholdValue.setEnabled(true);
thresholdIsMin.setEnabled(true);
}
graduatedColour = new JRadioButton(
MessageManager.getString("label.by_range_of") + COLON);
graduatedColour.setPreferredSize(new Dimension(RADIO_WIDTH, 20));
+ graduatedColour.setOpaque(false);
graduatedColour.addItemListener(new ItemListener()
{
@Override
{
if (minColour.isEnabled())
{
- String ttl = MessageManager.getString("label.select_colour_minimum_value");
+ String ttl = MessageManager
+ .getString("label.select_colour_minimum_value");
showColourChooser(minColour, ttl);
}
}
{
if (maxColour.isEnabled())
{
- String ttl = MessageManager.getString("label.select_colour_maximum_value");
+ String ttl = MessageManager
+ .getString("label.select_colour_maximum_value");
showColourChooser(maxColour, ttl);
}
}
maxColour.setBorder(new LineBorder(Color.black));
/*
- * default max colour to last plain colour;
- * 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.getColour() == null ? Color.BLACK : fc.getColour();
- maxColour.setBackground(bg);
- minColour.setBackground(ColorUtils.bleachColour(bg, 0.9f));
+ 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"));
thresholdValue_actionPerformed();
}
});
+ slider = new Slider(0f, 100f, 50f);
slider.setPaintLabels(false);
slider.setPaintTicks(true);
slider.setBackground(Color.white);
{
if (!adjusting)
{
- thresholdValue
- .setText(String.valueOf(slider.getValue() / scaleFactor));
+ setThresholdValueText(slider.getSliderValue());
+ thresholdValue.setBackground(Color.white); // to reset red for invalid
sliderValueChanged();
}
}
*/
if (ap != null)
{
- ap.paintAlignment(true, true);
+ refreshDisplay(true);
}
}
});
simpleColour = new JRadioButton(
MessageManager.getString("label.simple_colour"));
simpleColour.setPreferredSize(new Dimension(RADIO_WIDTH, 20));
+ simpleColour.setOpaque(false);
simpleColour.addItemListener(new ItemListener()
{
@Override
singleColour.setFont(JvSwingUtils.getLabelFont());
singleColour.setBorder(BorderFactory.createLineBorder(Color.black));
singleColour.setPreferredSize(new Dimension(40, 20));
- if (originalColour.isGraduatedColour())
- {
- singleColour.setBackground(originalColour.getMaxColour());
- singleColour.setForeground(originalColour.getMaxColour());
- }
- else
- {
- singleColour.setBackground(originalColour.getColour());
- singleColour.setForeground(originalColour.getColour());
- }
+ // if (originalColour.isGraduatedColour())
+ // {
+ // singleColour.setBackground(originalColour.getMaxColour());
+ // singleColour.setForeground(originalColour.getMaxColour());
+ // }
+ // else
+ // {
+ singleColour.setBackground(originalColour.getColour());
+ singleColour.setForeground(originalColour.getColour());
+ // }
singleColour.addMouseListener(new MouseAdapter()
{
@Override
{
if (simpleColour.isSelected())
{
- String ttl = MessageManager.formatMessage("label.select_colour_for", featureType);
+ String ttl = MessageManager
+ .formatMessage("label.select_colour_for", featureType);
showColourChooser(singleColour, ttl);
}
}
byCategory = new JRadioButton(
MessageManager.getString("label.by_text_of") + COLON);
byCategory.setPreferredSize(new Dimension(RADIO_WIDTH, 20));
+ byCategory.setOpaque(false);
byCategory.addItemListener(new ItemListener()
{
@Override
colourChanged(true);
}
};
- JalviewColourChooser.showColourChooser(this, title,
- colourPanel.getBackground(), listener);
+ JalviewColourChooser.showColourChooser(this, title,
+ colourPanel.getBackground(), listener);
}
/**
* save the colour, and repaint stuff
*/
fr.setColour(featureType, acg);
- ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
+ refreshDisplay(updateStructsAndOverview);
updateColoursPanel();
}
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 = singleColour.getBackground();
- FeatureColourI fc = new FeatureColour(c);
- 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
{
// 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;
int thresholdOption = threshold.getSelectedIndex();
{
maxValue = thresh;
}
+ Color noColour = null;
+ if (noValueCombo.getSelectedIndex() == MIN_COLOUR_OPTION)
+ {
+ noColour = minColour.getBackground();
+ }
+ else if (noValueCombo.getSelectedIndex() == MAX_COLOUR_OPTION)
+ {
+ noColour = maxColour.getBackground();
+ }
+
+ /*
+ * construct a colour that 'remembers' all the options, including
+ * those not currently selected
+ */
+ 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;
+ }
/*
- * make the graduated colour
+ * next easiest case - colour by Label, or attribute text
*/
- FeatureColourI fc = new FeatureColour(minColour.getBackground(),
- maxColour.getBackground(), noColour, minValue, maxValue);
+ 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();
{
fr.setColour(featureType, originalColour);
fr.setFeatureFilter(featureType, originalFilter);
- ap.paintAlignment(true, true);
+ refreshDisplay(true);
}
/**
{
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());
- slider.setValue((int) (f * scaleFactor));
+ f = Float.max(f, this.min);
+ f = Float.min(f, this.max);
+ setThresholdValueText(f);
+ slider.setSliderValue(f);
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;
}
}
/**
+ * Sets the text field for threshold value, rounded to four significant
+ * figures
+ *
+ * @param f
+ */
+ void setThresholdValueText(float f)
+ {
+ BigDecimal formatted = new BigDecimal(f).round(FOUR_SIG_FIG)
+ .stripTrailingZeros();
+ thresholdValue.setText(formatted.toPlainString());
+ }
+
+ /**
* 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 = getRoundedSliderValue();
+ threshline.value = slider.getSliderValue();
/*
* repaint alignment, but not Overview or structure,
colourChanged(false);
}
- /**
- * Converts the slider value to its absolute value by dividing by the
- * scaleFactor. Rounding errors are squashed by forcing min/max of slider
- * range to the actual min/max of feature score range
- *
- * @return
- */
- private float getRoundedSliderValue()
- {
- int value = slider.getValue();
- float f = value == slider.getMaximum() ? max
- : (value == slider.getMinimum() ? min : value / scaleFactor);
- return f;
- }
-
void addActionListener(ActionListener listener)
{
if (featureSettings != null)
{
- System.err.println(
+ jalview.bin.Console.errPrintln(
"IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
}
featureSettings = listener;
* @param withRange
* @param withText
*/
- protected JComboBox<String> populateAttributesDropdown(
+ protected JComboBox<Object> populateAttributesDropdown(
List<String[]> attNames, boolean withRange, boolean withText)
{
List<String> displayAtts = new ArrayList<>();
tooltips.add(desc == null ? "" : desc);
}
- JComboBox<String> attCombo = JvSwingUtils
- .buildComboWithTooltips(displayAtts, tooltips);
+ // now convert String List to Object List for buildComboWithTooltips
+ List<Object> displayAttsObjects = new ArrayList<>(displayAtts);
+ JComboBox<Object> attCombo = JvSwingUtils
+ .buildComboWithTooltips(displayAttsObjects, tooltips);
return attCombo;
}
andOrPanel.setBackground(Color.white);
andFilters = new JRadioButton(MessageManager.getString("label.and"));
orFilters = new JRadioButton(MessageManager.getString("label.or"));
+ andFilters.setOpaque(false);
+ orFilters.setOpaque(false);
ActionListener actionListener = new ActionListener()
{
@Override
* drop-down choice of attribute, with description as a tooltip
* if we can obtain it
*/
- JComboBox<String> attCombo = populateAttributesDropdown(attNames, true,
- true);
+ final JComboBox<Object> attCombo = populateAttributesDropdown(attNames,
+ true, true);
String filterBy = setSelectedAttribute(attCombo, filter);
JComboBox<Condition> condCombo = new JComboBox<>();
if (!patternField.isEnabled()
|| (pattern != null && pattern.trim().length() > 0))
{
- JButton removeCondition = new JButton("\u2717"); // Dingbats cursive x
+ JButton removeCondition = new JButton("\u2717");
+ // Dingbats cursive x
+ removeCondition.setBorder(new EmptyBorder(0, 0, 0, 0));
+ removeCondition.setBackground(Color.WHITE);
removeCondition.setPreferredSize(new Dimension(23, 17));
- removeCondition
- .setToolTipText(MessageManager.getString("label.delete_row"));
+ removeCondition.setToolTipText(
+ MessageManager.getString("label.delete_condition"));
removeCondition.addActionListener(new ActionListener()
{
@Override
* @param attCombo
* @param filter
*/
- private String setSelectedAttribute(JComboBox<String> attCombo,
+ private String setSelectedAttribute(JComboBox<Object> attCombo,
FeatureMatcherI filter)
{
String item = null;
* @param valueField
* @param filterIndex
*/
- protected boolean updateFilter(JComboBox<String> attCombo,
+ protected boolean updateFilter(JComboBox<Object> attCombo,
JComboBox<Condition> condCombo, JTextField valueField,
int filterIndex)
{
- String attName = (String) attCombo.getSelectedItem();
+ String attName;
+ try
+ {
+ attName = (String) attCombo.getSelectedItem();
+ } catch (Exception e)
+ {
+ Console.error("Problem casting Combo box entry to String");
+ attName = attCombo.getSelectedItem().toString();
+ }
Condition cond = (Condition) condCombo.getSelectedItem();
String pattern = valueField.getText().trim();
* (note this might now be an empty filter with no conditions)
*/
fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
- ap.paintAlignment(true, true);
+ refreshDisplay(true);
updateFiltersPanel();
}
+
+ /**
+ * Repaints alignment, structure and overview (if shown). If there is a
+ * complementary view which is showing this view's features, then also
+ * repaints that.
+ *
+ * @param updateStructsAndOverview
+ */
+ void refreshDisplay(boolean updateStructsAndOverview)
+ {
+ ap.paintAlignment(true, updateStructsAndOverview);
+ AlignViewportI complement = ap.getAlignViewport().getCodingComplement();
+ if (complement != null && complement.isShowComplementFeatures())
+ {
+ AlignFrame af2 = Desktop.getAlignFrameFor(complement);
+ af2.alignPanel.paintAlignment(true, updateStructsAndOverview);
+ }
+ }
}