import jalview.datamodel.features.FeatureMatcherI;
import jalview.datamodel.features.FeatureMatcherSet;
import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.io.gff.SequenceOntologyFactory;
import jalview.schemes.FeatureColour;
import jalview.util.ColorUtils;
import jalview.util.MessageManager;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
/*
* the colour and filters to reset to on Cancel
+ * (including feature sub-types if modified)
*/
- private final FeatureColourI originalColour;
+ private Map<String, FeatureColourI> originalColours;
- private final FeatureMatcherSetI originalFilter;
+ private Map<String, FeatureMatcherSetI> originalFilters;
/*
* set flag to true when setting values programmatically,
private JPanel chooseFiltersPanel;
+ /*
+ * feature types present in Feature Renderer which are
+ * sub-types of the one this editor is acting on
+ */
+ private final List<String> subTypes;
+
+ /*
+ * if true, filter or colour settings are also applied to
+ * any feature sub-types in the Sequence Ontology
+ */
+ private boolean applyFiltersToSubtypes;
+
+ private boolean applyColourToSubtypes;
+
/**
* Constructor
*
this.fr = frender;
this.featureType = theType;
ap = fr.ap;
- originalFilter = fr.getFeatureFilter(theType);
- originalColour = fr.getFeatureColours().get(theType);
-
+
+ /*
+ * 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
+
+ /*
+ * save original colours and filters for this feature type
+ * and any sub-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)
+ {
+ originalFilters.put(child, fr.getFeatureFilter(child));
+ originalColours.put(child, fr.getFeatureColours().get(child));
+ }
+
adjusting = true;
try
MessageManager.getString("action.colour"), true);
/*
+ * option to apply colour to sub-types as well (if there are any)
+ */
+ if (!subTypes.isEmpty())
+ {
+ applyColourToSubtypes = false;
+ colourByPanel.add(initSubtypesPanel(false));
+ }
+
+ /*
* simple colour radio button and colour picker
*/
JPanel simpleColourPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
return colourByPanel;
}
+ /**
+ * Constructs and returns a panel with a checkbox for the option to apply any
+ * changes also to sub-types of the feature type
+ *
+ * @return
+ */
+ protected JPanel initSubtypesPanel(final boolean forFilters)
+ {
+ JPanel toSubtypes = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ toSubtypes.setBackground(Color.WHITE);
+ JCheckBox applyToSubtypesCB = new JCheckBox(MessageManager
+ .formatMessage("label.apply_to_subtypes", featureType));
+ applyToSubtypesCB.setToolTipText(getSubtypesTooltip());
+ applyToSubtypesCB.addActionListener(new ActionListener()
+ {
+ /*
+ * reset and reapply settings on toggle of checkbox
+ */
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ if (forFilters)
+ {
+ applyFiltersToSubtypes = applyToSubtypesCB.isSelected();
+ restoreOriginalFilters();
+ filtersChanged();
+ }
+ else
+ {
+ applyColourToSubtypes = applyToSubtypesCB.isSelected();
+ restoreOriginalColours();
+ colourChanged(true);
+ }
+ }
+ });
+ toSubtypes.add(applyToSubtypesCB);
+
+ return toSubtypes;
+ }
+
private void showColourChooser(JPanel colourPanel, String key)
{
Color col = JColorChooser.showDialog(this,
FeatureColourI acg = makeColourFromInputs();
/*
- * save the colour, and repaint stuff
+ * save the colour, and set on subtypes if selected
*/
fr.setColour(featureType, acg);
+ if (applyColourToSubtypes)
+ {
+ for (String child : subTypes)
+ {
+ fr.setColour(child, acg);
+ }
+ }
+ refreshFeatureSettings();
ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
updateColoursTab();
@Override
protected void raiseClosed()
{
+ refreshFeatureSettings();
+ }
+
+ protected void refreshFeatureSettings()
+ {
if (this.featureSettings != null)
{
- featureSettings.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
+ featureSettings.actionPerformed(new ActionEvent(this, 0, "REFRESH"));
}
}
/**
* Action on Cancel is to restore colour scheme and filters as they were when
- * the dialog was opened
+ * the dialog was opened (including any feature sub-types that may have been
+ * changed)
*/
@Override
public void cancelPressed()
{
- fr.setColour(featureType, originalColour);
- fr.setFeatureFilter(featureType, originalFilter);
+ restoreOriginalColours();
+ restoreOriginalFilters();
ap.paintAlignment(true, true);
}
+ protected void restoreOriginalFilters()
+ {
+ for (Entry<String, FeatureMatcherSetI> entry : originalFilters
+ .entrySet())
+ {
+ fr.setFeatureFilter(entry.getKey(), entry.getValue());
+ }
+ }
+
+ protected void restoreOriginalColours()
+ {
+ for (Entry<String, FeatureColourI> entry : originalColours.entrySet())
+ {
+ fr.setColour(entry.getKey(), entry.getValue());
+ }
+ }
+
/**
* Action on text entry of a threshold value
*/
{
filters = new ArrayList<>();
+ JPanel outerPanel = new JPanel();
+ outerPanel.setLayout(new BoxLayout(outerPanel, BoxLayout.Y_AXIS));
+ outerPanel.setBackground(Color.white);
+
+ /*
+ * option to apply colour to sub-types as well (if there are any)
+ */
+ if (!subTypes.isEmpty())
+ {
+ applyFiltersToSubtypes = false;
+ outerPanel.add(initSubtypesPanel(true));
+ }
+
JPanel filtersPanel = new JPanel();
filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
filtersPanel.setBackground(Color.white);
JvSwingUtils.createTitledBorder(filtersPanel,
MessageManager.getString("label.filters"), true);
+ outerPanel.add(filtersPanel);
JPanel andOrPanel = initialiseAndOrPanel();
filtersPanel.add(andOrPanel);
chooseFiltersPanel.setBackground(Color.white);
filtersPanel.add(chooseFiltersPanel);
- return filtersPanel;
+ return outerPanel;
}
/**
*/
private JPanel initialiseAndOrPanel()
{
- JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ 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()
new JLabel(MessageManager.getString("label.join_conditions")));
andOrPanel.add(andFilters);
andOrPanel.add(orFilters);
+
return andOrPanel;
}
/**
+ * Builds a tooltip for the 'Apply to subtypes' checkbox with a list of
+ * subtypes of this feature type
+ *
+ * @return
+ */
+ protected String getSubtypesTooltip()
+ {
+ StringBuilder sb = new StringBuilder(20 * subTypes.size());
+ sb.append(MessageManager.getString("label.apply_also_to"));
+ for (String child : subTypes)
+ {
+ sb.append("<br>").append(child);
+ }
+ String tooltip = JvSwingUtils.wrapTooltip(true, sb.toString());
+ return tooltip;
+ }
+
+ /**
* Refreshes the display to show any filters currently configured for the
* selected feature type (editable, with 'remove' option), plus one extra row
* for adding a condition. This should be called after a filter has been
* (note this might now be an empty filter with no conditions)
*/
fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
+ if (applyFiltersToSubtypes)
+ {
+ for (String child : subTypes)
+ {
+ fr.setFeatureFilter(child, combined.isEmpty() ? null : combined);
+ }
+ }
+
+ refreshFeatureSettings();
ap.paintAlignment(true, true);
updateFiltersTab();