import jalview.api.AlignmentViewPanel;
import jalview.api.FeatureColourI;
+import jalview.bin.Cache;
import jalview.datamodel.GraphLine;
import jalview.datamodel.features.FeatureAttributes;
import jalview.datamodel.features.FeatureAttributes.Datatype;
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;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.JTextField;
-import javax.swing.SwingConstants;
+import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
-import javax.swing.plaf.basic.BasicArrowButton;
/**
* A dialog where the user can configure colour scheme, and any filters, for one
private JPanel maxColour = new JPanel();
- private JComboBox<String> threshold = new JComboBox<>();
+ private JComboBox<Object> threshold = new JComboBox<>();
private JSlider slider = new JSlider();
/*
* 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;
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;
+
+ /*
+ * a map whose keys are Sequence Ontology terms - selected from the
+ * current term and its parents in the SO - whose subterms include
+ * additional feature types; the map entry is the list of additional
+ * feature types that match the key or have it as a parent term; in
+ * other words, distinct 'aggregations' that include the current feature type
+ */
+ private final Map<String, List<String>> relatedSoTerms;
/*
* if true, filter or colour settings are also applied to
- * any feature sub-types in the Sequence Ontology
+ * any sub-types of parentTerm in the Sequence Ontology
*/
private boolean applyFiltersToSubtypes;
private boolean applyColourToSubtypes;
+ private String parentSOTerm;
+
/**
* Constructor
*
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
+ SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+ relatedSoTerms = so.findSequenceOntologyGroupings(
+ this.featureType, fr.getRenderOrder());
/*
- * save original colours and filters for this feature type
- * and any sub-types, to restore on Cancel
+ * save original colours and filters for this feature type,
+ * and any related types, to restore on Cancel
*/
originalFilters = new HashMap<>();
originalFilters.put(theType, fr.getFeatureFilter(theType));
originalColours = new HashMap<>();
originalColours.put(theType, fr.getFeatureColours().get(theType));
- for (String child : subTypes)
+ for (List<String> related : relatedSoTerms.values())
{
- originalFilters.put(child, fr.getFeatureFilter(child));
- originalColours.put(child, fr.getFeatureColours().get(child));
+ for (String type : related)
+ {
+ originalFilters.put(type, fr.getFeatureFilter(type));
+ originalColours.put(type, fr.getFeatureColours().get(type));
+ }
}
adjusting = true;
-
+
try
{
initialise();
ex.printStackTrace();
return;
}
-
+
updateColoursTab();
-
+
updateFiltersTab();
-
+
adjusting = false;
-
+
colourChanged(false);
-
+
String title = MessageManager
.formatMessage("label.display_settings_for", new String[]
{ theType });
}
/**
+ * Answers a (possibly empty) map of any Sequence Ontology terms (the current
+ * feature type and its parents) which incorporate additional known feature
+ * types (the map entry).
+ * <p>
+ * For example if {@code stop_gained} and {@code stop_lost} are known feature
+ * types, then SO term {@ nonsynonymous_variant} is the first common parent of
+ * both terms
+ *
+ * @param featureType
+ * the current feature type being configured
+ * @param featureTypes
+ * all known feature types on the alignment
+ * @return
+ */
+ protected static Map<String, List<String>> findSequenceOntologyGroupings(
+ String featureType, List<String> featureTypes)
+ {
+ List<String> sortedTypes = new ArrayList<>(featureTypes);
+ Collections.sort(sortedTypes);
+
+ Map<String, List<String>> parents = new HashMap<>();
+
+ /*
+ * method:
+ * walk up featureType and all of its parents
+ * find other feature types which are subsumed by each term
+ * add each distinct aggregation of included feature types to the map
+ */
+ List<String> candidates = new ArrayList<>();
+ SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+ candidates.add(featureType);
+ while (!candidates.isEmpty())
+ {
+ String term = candidates.remove(0);
+ List<String> includedFeatures = new ArrayList<>();
+ for (String type : sortedTypes)
+ {
+ if (!type.equals(featureType) && so.isA(type, term))
+ {
+ includedFeatures.add(type);
+ }
+ }
+ if (!includedFeatures.isEmpty()
+ && !parents.containsValue(includedFeatures))
+ {
+ parents.put(term, includedFeatures);
+ }
+ candidates.addAll(so.getParents(term));
+ }
+
+ return parents;
+ }
+
+ /**
* Configures the widgets on the Colours tab according to the current feature
* colour scheme
*/
MessageManager.getString("action.colour"), true);
/*
- * option to apply colour to sub-types as well (if there are any)
+ * option to apply colour to other selected types as well
*/
- if (!subTypes.isEmpty())
+ if (!relatedSoTerms.isEmpty())
{
applyColourToSubtypes = false;
colourByPanel.add(initSubtypesPanel(false));
}
/**
- * Constructs and returns a panel with a checkbox for the option to apply any
- * changes also to sub-types of the feature type
+ * Constructs and returns a panel with the option to apply any changes also to
+ * sub-types of SO terms at or above the feature type
*
* @return
*/
{
JPanel toSubtypes = new JPanel(new FlowLayout(FlowLayout.LEFT));
toSubtypes.setBackground(Color.WHITE);
+
+ /*
+ * checkbox 'apply to sub-types of...'
+ */
JCheckBox applyToSubtypesCB = new JCheckBox(MessageManager
- .formatMessage("label.apply_to_subtypes", featureType));
- applyToSubtypesCB.setToolTipText(getSubtypesTooltip());
- applyToSubtypesCB.addActionListener(new ActionListener()
+ .formatMessage("label.apply_to_subtypes", rootSOTerm));
+ toSubtypes.add(applyToSubtypesCB);
+ toSubtypes
+ .setToolTipText(MessageManager.getString("label.group_by_so"));
+
+ /*
+ * combobox to choose 'parent' of sub-types
+ */
+ List<String> soTerms = new ArrayList<>();
+ for (String term : relatedSoTerms.keySet())
+ {
+ soTerms.add(term);
+ }
+ // sort from most restrictive to most inclusive
+ Collections.sort(soTerms, new Comparator<String>()
+ {
+ @Override
+ public int compare(String o1, String o2)
+ {
+ return Integer.compare(relatedSoTerms.get(o1).size(),
+ relatedSoTerms.get(o2).size());
+ }
+ });
+ List<String> tooltips = new ArrayList<>();
+ for (String term : soTerms)
+ {
+ tooltips.add(getSOTermsTooltip(relatedSoTerms.get(term)));
+ }
+ JComboBox<String> parentType = JvSwingUtils
+ .buildComboWithTooltips(soTerms, tooltips);
+ toSubtypes.add(parentType);
+
+ /*
+ * on toggle of checkbox, or change of parent SO term,
+ * reset and then reapply filters to the selected scope
+ */
+ final ActionListener action = new ActionListener()
{
/*
* reset and reapply settings on toggle of checkbox
@Override
public void actionPerformed(ActionEvent e)
{
+ parentSOTerm = (String) parentType.getSelectedItem();
if (forFilters)
{
applyFiltersToSubtypes = applyToSubtypesCB.isSelected();
colourChanged(true);
}
}
- });
- toSubtypes.add(applyToSubtypesCB);
+ };
+ applyToSubtypesCB.addActionListener(action);
+ parentType.addActionListener(action);
return toSubtypes;
}
fr.setColour(featureType, acg);
if (applyColourToSubtypes)
{
- for (String child : subTypes)
+ for (String child : relatedSoTerms.get(parentSOTerm))
{
fr.setColour(child, acg);
}
ap.paintAlignment(true, true);
}
+ /**
+ * Restores filters for all feature types to their values when the dialog was
+ * opened
+ */
protected void restoreOriginalFilters()
{
for (Entry<String, FeatureMatcherSetI> entry : originalFilters
}
}
+ /**
+ * Restores colours for all feature types to their values when the dialog was
+ * opened
+ */
protected void restoreOriginalColours()
{
for (Entry<String, FeatureColourI> entry : originalColours.entrySet())
*/
adjusting = true;
float f = Float.parseFloat(thresholdValue.getText());
- f = Float.max(f, this.min);
+ f = Float.max(f, this.min);
f = Float.min(f, this.max);
thresholdValue.setText(String.valueOf(f));
slider.setValue((int) (f * scaleFactor));
* @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;
}
outerPanel.setBackground(Color.white);
/*
- * option to apply colour to sub-types as well (if there are any)
+ * option to apply colour to other selected types as well
*/
- if (!subTypes.isEmpty())
+ if (!relatedSoTerms.isEmpty())
{
applyFiltersToSubtypes = false;
outerPanel.add(initSubtypesPanel(true));
JPanel andOrPanel = new JPanel(new BorderLayout());
andOrPanel.setBackground(Color.white);
-// JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
-// andOrPanel.add(panel1, BorderLayout.WEST);
-// panel1.setBackground(Color.white);
-// panel1.setBorder(BorderFactory.createLineBorder(debugBorderColour));
andFilters = new JRadioButton(MessageManager.getString("label.and"));
orFilters = new JRadioButton(MessageManager.getString("label.or"));
ActionListener actionListener = new ActionListener()
}
/**
- * Builds a tooltip for the 'Apply to subtypes' checkbox with a list of
- * subtypes of this feature type
+ * Builds a tooltip for the 'Apply also to...' combobox with a list of known
+ * feature types (excluding the current type) which are sub-types of the
+ * selected Sequence Ontology term
*
+ * @param
* @return
*/
- protected String getSubtypesTooltip()
+ protected String getSOTermsTooltip(List<String> list)
{
- StringBuilder sb = new StringBuilder(20 * subTypes.size());
+ StringBuilder sb = new StringBuilder(20 * relatedSoTerms.size());
sb.append(MessageManager.getString("label.apply_also_to"));
- for (String child : subTypes)
+ for (String child : list)
{
sb.append("<br>").append(child);
}
* drop-down choice of attribute, with description as a tooltip
* if we can obtain it
*/
- final JComboBox<String> attCombo = populateAttributesDropdown(attNames,
+ final JComboBox<Object> attCombo = populateAttributesDropdown(attNames,
true, true);
String filterBy = setSelectedAttribute(attCombo, filter);
if (!patternField.isEnabled()
|| (pattern != null && pattern.trim().length() > 0))
{
- // todo: gif for button drawing '-' or 'x'
- JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
- removeCondition
- .setToolTipText(MessageManager.getString("label.delete_row"));
+ JButton removeCondition = new JButton("\u2717"); // Dingbats cursive x
+ removeCondition.setToolTipText(
+ MessageManager.getString("label.delete_condition"));
+ removeCondition.setBorder(new EmptyBorder(0, 0, 0, 0));
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)
+ {
+ Cache.log.error("Problem casting Combo box entry to String");
+ attName = attCombo.getSelectedItem().toString();
+ }
Condition cond = (Condition) condCombo.getSelectedItem();
String pattern = valueField.getText().trim();
fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
if (applyFiltersToSubtypes)
{
- for (String child : subTypes)
+ for (String child : relatedSoTerms.get(parentSOTerm))
{
fr.setFeatureFilter(child, combined.isEmpty() ? null : combined);
}