X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FFeatureColourChooser.java;h=3fc3116f2588578322ce60382d224e2b83b2e53c;hb=14307f5cfcbc90c419c892434613a500ca550ecc;hp=b27328d2c947af7f9acb948143d4870736eb6ea6;hpb=be762d8d9c71a7aa3121e845c45911c7192b7827;p=jalview.git diff --git a/src/jalview/gui/FeatureColourChooser.java b/src/jalview/gui/FeatureColourChooser.java index b27328d..3fc3116 100644 --- a/src/jalview/gui/FeatureColourChooser.java +++ b/src/jalview/gui/FeatureColourChooser.java @@ -22,10 +22,10 @@ package jalview.gui; import jalview.api.FeatureColourI; import jalview.datamodel.GraphLine; +import jalview.datamodel.features.FeatureAttributes; import jalview.schemes.FeatureColour; import jalview.util.MessageManager; -import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; @@ -33,15 +33,24 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.JComboBox; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JRadioButton; import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.border.LineBorder; @@ -50,7 +59,8 @@ import javax.swing.event.ChangeListener; public class FeatureColourChooser extends JalviewDialog { - // FeatureSettings fs; + private static final int MAX_TOOLTIP_LENGTH = 50; + private FeatureRenderer fr; private FeatureColourI cs; @@ -61,11 +71,11 @@ public class FeatureColourChooser extends JalviewDialog private boolean adjusting = false; - final private float min; + private float min; - final private float max; + private float max; - final private float scaleFactor; + private float scaleFactor; private String type = null; @@ -73,27 +83,50 @@ public class FeatureColourChooser extends JalviewDialog private JPanel maxColour = new JPanel(); + private JPanel noColour = new JPanel(); + private JComboBox threshold = new JComboBox<>(); private JSlider slider = new JSlider(); private JTextField thresholdValue = new JTextField(20); - // TODO implement GUI for tolower flag - // JCheckBox toLower = new JCheckBox(); - private JCheckBox thresholdIsMin = new JCheckBox(); - private JCheckBox colourByLabel = new JCheckBox(); - private GraphLine threshline; private Color oldmaxColour; private Color oldminColour; + private Color oldNoColour; + private ActionListener colourEditor = null; + /* + * radio buttons to select what to colour by + * label, attribute text, score, attribute value + */ + private JRadioButton byDescription = new JRadioButton(); + + private JRadioButton byAttributeText = new JRadioButton(); + + private JRadioButton byScore = new JRadioButton(); + + private JRadioButton byAttributeValue = new JRadioButton(); + + private ActionListener changeColourAction; + + /* + * choice of attribute (if any) for 'colour by text' + */ + private JComboBox textAttributeCombo; + + /* + * choice of attribute (if any) for 'colour by value' + */ + private JComboBox valueAttributeCombo; + /** * Constructor * @@ -122,7 +155,7 @@ public class FeatureColourChooser extends JalviewDialog String title = MessageManager .formatMessage("label.graduated_color_for_params", new String[] { theType }); - initDialogFrame(this, true, blocking, title, 480, 185); + initDialogFrame(this, true, blocking, title, 450, 300); slider.addChangeListener(new ChangeListener() { @@ -147,7 +180,7 @@ public class FeatureColourChooser extends JalviewDialog */ if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } }); @@ -178,7 +211,10 @@ public class FeatureColourChooser extends JalviewDialog } else { - // promote original color to a graduated color + /* + * promote original simple color to a graduated color + * - by score if there is a score range, else by label + */ Color bl = oldcs.getColour(); if (bl == null) { @@ -186,10 +222,11 @@ public class FeatureColourChooser extends JalviewDialog } // original colour becomes the maximum colour cs = new FeatureColour(Color.white, bl, mm[0], mm[1]); - cs.setColourByLabel(false); + cs.setColourByLabel(mm[0] == mm[1]); } minColour.setBackground(oldminColour = cs.getMinColour()); maxColour.setBackground(oldmaxColour = cs.getMaxColour()); + noColour.setBackground(oldNoColour = cs.getNoColour()); adjusting = true; try @@ -197,10 +234,46 @@ public class FeatureColourChooser extends JalviewDialog jbInit(); } catch (Exception ex) { + ex.printStackTrace(); + return; } - // update the gui from threshold state + + /* + * set the initial state of options on screen + */ thresholdIsMin.setSelected(!cs.isAutoScaled()); - colourByLabel.setSelected(cs.isColourByLabel()); + + if (cs.isColourByLabel()) + { + if (cs.isColourByAttribute()) + { + byAttributeText.setSelected(true); + textAttributeCombo.setEnabled(true); + textAttributeCombo.setSelectedItem(cs.getAttributeName()); + } + else + { + byDescription.setSelected(true); + textAttributeCombo.setEnabled(false); + } + } + else + { + if (cs.isColourByAttribute()) + { + byAttributeValue.setSelected(true); + String attributeName = cs.getAttributeName(); + valueAttributeCombo.setSelectedItem(attributeName); + valueAttributeCombo.setEnabled(true); + setAttributeMinMax(attributeName); + } + else + { + byScore.setSelected(true); + valueAttributeCombo.setEnabled(false); + } + } + if (cs.hasThreshold()) { // initialise threshold slider and selector @@ -219,8 +292,104 @@ public class FeatureColourChooser extends JalviewDialog waitForInput(); } - private void jbInit() throws Exception + /** + * Configures the initial layout + */ + private void jbInit() { + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.setBackground(Color.white); + + changeColourAction = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + changeColour(true); + } + }; + + /* + * this panel + * detailsPanel + * colourByTextPanel + * colourByScorePanel + * okCancelPanel + */ + JPanel detailsPanel = new JPanel(); + detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS)); + + JPanel colourByTextPanel = initColourByTextPanel(); + detailsPanel.add(colourByTextPanel); + + JPanel colourByValuePanel = initColourByValuePanel(); + detailsPanel.add(colourByValuePanel); + + /* + * 4 radio buttons select between colour by description, by + * attribute text, by score, or by attribute value + */ + ButtonGroup bg = new ButtonGroup(); + bg.add(byDescription); + bg.add(byAttributeText); + bg.add(byScore); + bg.add(byAttributeValue); + + JPanel okCancelPanel = initOkCancelPanel(); + + this.add(detailsPanel); + this.add(okCancelPanel); + } + + /** + * Lay out fields for graduated colour by value + * + * @return + */ + protected JPanel initColourByValuePanel() + { + JPanel byValuePanel = new JPanel(); + byValuePanel.setLayout(new BoxLayout(byValuePanel, BoxLayout.Y_AXIS)); + byValuePanel.setBorder(BorderFactory.createTitledBorder(MessageManager + .getString("label.colour_by_value"))); + byValuePanel.setBackground(Color.white); + + /* + * first row - choose colour by score or by attribute, choose attribute + */ + JPanel byWhatPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + byWhatPanel.setBackground(Color.white); + byValuePanel.add(byWhatPanel); + + byScore.setText(MessageManager.getString("label.score")); + byWhatPanel.add(byScore); + byScore.addActionListener(changeColourAction); + + byAttributeValue.setText(MessageManager +.getString("label.attribute")); + byAttributeValue.addActionListener(changeColourAction); + byWhatPanel.add(byAttributeValue); + + List attNames = FeatureAttributes.getInstance().getAttributes( + type); + valueAttributeCombo = populateAttributesDropdown(type, attNames, + true); + + /* + * if no numeric atttibutes found, disable colour by attribute value + */ + if (valueAttributeCombo.getItemCount() == 0) + { + byAttributeValue.setEnabled(false); + } + + byWhatPanel.add(valueAttributeCombo); + + /* + * second row - min/max/no colours + */ + JPanel colourRangePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + colourRangePanel.setBackground(Color.white); + byValuePanel.add(colourRangePanel); minColour.setFont(JvSwingUtils.getLabelFont()); minColour.setBorder(BorderFactory.createLineBorder(Color.black)); @@ -237,6 +406,7 @@ public class FeatureColourChooser extends JalviewDialog } } }); + maxColour.setFont(JvSwingUtils.getLabelFont()); maxColour.setBorder(BorderFactory.createLineBorder(Color.black)); maxColour.setPreferredSize(new Dimension(40, 20)); @@ -253,24 +423,62 @@ public class FeatureColourChooser extends JalviewDialog } }); maxColour.setBorder(new LineBorder(Color.black)); - JLabel minText = new JLabel(MessageManager.getString("label.min")); - minText.setFont(JvSwingUtils.getLabelFont()); - JLabel maxText = new JLabel(MessageManager.getString("label.max")); - maxText.setFont(JvSwingUtils.getLabelFont()); - this.setLayout(new BorderLayout()); - JPanel jPanel1 = new JPanel(); - jPanel1.setBackground(Color.white); - JPanel jPanel2 = new JPanel(); - jPanel2.setLayout(new FlowLayout()); - jPanel2.setBackground(Color.white); - threshold.addActionListener(new ActionListener() + + noColour.setFont(JvSwingUtils.getLabelFont()); + noColour.setBorder(BorderFactory.createLineBorder(Color.black)); + noColour.setPreferredSize(new Dimension(40, 20)); + noColour.setToolTipText("Colour if feature has no attribute value"); + noColour.addMouseListener(new MouseAdapter() { @Override - public void actionPerformed(ActionEvent e) + public void mousePressed(MouseEvent e) + { + if (e.isPopupTrigger()) // Mac: mouseReleased + { + showNoColourPopup(e); + return; + } + if (noColour.isEnabled()) + { + noColour_actionPerformed(); + } + } + + @Override + public void mouseReleased(MouseEvent e) { - threshold_actionPerformed(); + if (e.isPopupTrigger()) // Windows: mouseReleased + { + showNoColourPopup(e); + e.consume(); + return; + } } }); + noColour.setBorder(new LineBorder(Color.black)); + + JLabel minText = new JLabel(MessageManager.getString("label.min")); + minText.setFont(JvSwingUtils.getLabelFont()); + JLabel maxText = new JLabel(MessageManager.getString("label.max")); + maxText.setFont(JvSwingUtils.getLabelFont()); + JLabel noText = new JLabel(MessageManager.getString("label.no_colour")); + noText.setFont(JvSwingUtils.getLabelFont()); + + colourRangePanel.add(minText); + colourRangePanel.add(minColour); + colourRangePanel.add(maxText); + colourRangePanel.add(maxColour); + colourRangePanel.add(noText); + colourRangePanel.add(noColour); + + /* + * third row - threshold options and value + */ + JPanel thresholdPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + thresholdPanel.setBackground(Color.white); + byValuePanel.add(thresholdPanel); + + threshold.addActionListener(changeColourAction); threshold.setToolTipText(MessageManager .getString("label.threshold_feature_display_by_score")); threshold.addItem(MessageManager @@ -280,8 +488,6 @@ public class FeatureColourChooser extends JalviewDialog threshold.addItem(MessageManager .getString("label.threshold_feature_below_threshold")); // index 2 - JPanel jPanel3 = new JPanel(); - jPanel3.setLayout(new FlowLayout()); thresholdValue.addActionListener(new ActionListener() { @Override @@ -308,51 +514,117 @@ public class FeatureColourChooser extends JalviewDialog MessageManager.getString("label.adjust_threshold")); thresholdValue.setEnabled(false); thresholdValue.setColumns(7); - jPanel3.setBackground(Color.white); + + thresholdPanel.add(threshold); + thresholdPanel.add(slider); + thresholdPanel.add(thresholdValue); + + /* + * 4th row - threshold is min / max + */ + JPanel isMinMaxPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + isMinMaxPanel.setBackground(Color.white); + byValuePanel.add(isMinMaxPanel); thresholdIsMin.setBackground(Color.white); thresholdIsMin .setText(MessageManager.getString("label.threshold_minmax")); thresholdIsMin.setToolTipText(MessageManager .getString("label.toggle_absolute_relative_display_threshold")); - thresholdIsMin.addActionListener(new ActionListener() + thresholdIsMin.addActionListener(changeColourAction); + isMinMaxPanel.add(thresholdIsMin); + + return byValuePanel; + } + + /** + * Show a popup menu with options to make 'no value colour' the same as Min + * Colour or Max Colour + * + * @param evt + */ + protected void showNoColourPopup(MouseEvent evt) + { + JPopupMenu pop = new JPopupMenu(); + + JMenuItem copyMin = new JMenuItem( + MessageManager.getString("label.min_colour")); + copyMin.addActionListener((new ActionListener() { @Override - public void actionPerformed(ActionEvent actionEvent) + public void actionPerformed(ActionEvent e) { - thresholdIsMin_actionPerformed(); + noColour.setBackground(minColour.getBackground()); + changeColour(true); } - }); - colourByLabel.setBackground(Color.white); - colourByLabel - .setText(MessageManager.getString("label.colour_by_label")); - colourByLabel.setToolTipText(MessageManager.getString( - "label.display_features_same_type_different_label_using_different_colour")); - colourByLabel.addActionListener(new ActionListener() + })); + pop.add(copyMin); + + JMenuItem copyMax = new JMenuItem( + MessageManager.getString("label.max_colour")); + copyMax.addActionListener((new ActionListener() { @Override - public void actionPerformed(ActionEvent actionEvent) + public void actionPerformed(ActionEvent e) { - colourByLabel_actionPerformed(); + noColour.setBackground(maxColour.getBackground()); + changeColour(true); } - }); + })); + pop.add(copyMax); + + pop.show(noColour, evt.getX(), evt.getY()); + } + + /** + * Lay out OK and Cancel buttons + * + * @return + */ + protected JPanel initOkCancelPanel() + { + JPanel okCancelPanel = new JPanel(); + okCancelPanel.setBackground(Color.white); + okCancelPanel.add(ok); + okCancelPanel.add(cancel); + return okCancelPanel; + } + + /** + * Lay out Colour by Label and attribute choice elements + * + * @return + */ + protected JPanel initColourByTextPanel() + { + JPanel byTextPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + byTextPanel.setBackground(Color.white); + byTextPanel.setBorder(BorderFactory.createTitledBorder(MessageManager + .getString("label.colour_by_text"))); + + byDescription.setText(MessageManager.getString("label.label")); + byDescription.setToolTipText(MessageManager + .getString("label.colour_by_label_tip")); + byDescription.addActionListener(changeColourAction); + byTextPanel.add(byDescription); + + byAttributeText.setText(MessageManager.getString("label.attribute")); + byAttributeText.addActionListener(changeColourAction); + byTextPanel.add(byAttributeText); + + List 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); + } - JPanel colourPanel = new JPanel(); - colourPanel.setBackground(Color.white); - jPanel1.add(ok); - jPanel1.add(cancel); - jPanel2.add(colourByLabel, BorderLayout.WEST); - jPanel2.add(colourPanel, BorderLayout.EAST); - colourPanel.add(minText); - colourPanel.add(minColour); - colourPanel.add(maxText); - colourPanel.add(maxColour); - this.add(jPanel3, BorderLayout.CENTER); - jPanel3.add(threshold); - jPanel3.add(slider); - jPanel3.add(thresholdValue); - jPanel3.add(thresholdIsMin); - this.add(jPanel1, BorderLayout.SOUTH); - this.add(jPanel2, BorderLayout.NORTH); + return byTextPanel; } /** @@ -392,13 +664,31 @@ public class FeatureColourChooser extends JalviewDialog } /** + * Action on clicking the 'no colour' - open a colour chooser dialog, and set + * the selected colour (if the user does not cancel out of the dialog) + */ + protected void noColour_actionPerformed() + { + Color col = JColorChooser.showDialog(this, + MessageManager.getString("label.select_no_value_colour"), + noColour.getBackground()); + if (col != null) + { + noColour.setBackground(col); + noColour.setForeground(col); + } + noColour.repaint(); + changeColour(true); + } + + /** * 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 updateOverview + * @param updateStructsAndOverview */ - void changeColour(boolean updateOverview) + void changeColour(boolean updateStructsAndOverview) { // Check if combobox is still adjusting if (adjusting) @@ -421,6 +711,9 @@ public class FeatureColourChooser extends JalviewDialog slider.setEnabled(true); thresholdValue.setEnabled(true); + /* + * make the feature colour + */ FeatureColourI acg; if (cs.isColourByLabel()) { @@ -429,8 +722,23 @@ public class FeatureColourChooser extends JalviewDialog else { acg = new FeatureColour(oldminColour = minColour.getBackground(), - oldmaxColour = maxColour.getBackground(), min, max); + oldmaxColour = maxColour.getBackground(), + oldNoColour = noColour.getBackground(), min, max); } + String attribute = null; + textAttributeCombo.setEnabled(false); + valueAttributeCombo.setEnabled(false); + if (byAttributeText.isSelected()) + { + attribute = (String) textAttributeCombo.getSelectedItem(); + textAttributeCombo.setEnabled(true); + } + else if (byAttributeValue.isSelected()) + { + attribute = (String) valueAttributeCombo.getSelectedItem(); + valueAttributeCombo.setEnabled(true); + } + acg.setAttributeName(attribute); if (!hasThreshold) { @@ -463,7 +771,7 @@ public class FeatureColourChooser extends JalviewDialog slider.setMajorTickSpacing((int) (range / 10f)); slider.setEnabled(true); thresholdValue.setEnabled(true); - thresholdIsMin.setEnabled(!colourByLabel.isSelected()); + thresholdIsMin.setEnabled(!byDescription.isSelected()); adjusting = false; } @@ -485,29 +793,40 @@ public class FeatureColourChooser extends JalviewDialog { acg.setAutoScaled(true); } - acg.setColourByLabel(colourByLabel.isSelected()); + acg.setColourByLabel(byDescription.isSelected() + || byAttributeText.isSelected()); + if (acg.isColourByLabel()) { maxColour.setEnabled(false); minColour.setEnabled(false); + noColour.setEnabled(false); maxColour.setBackground(this.getBackground()); maxColour.setForeground(this.getBackground()); minColour.setBackground(this.getBackground()); minColour.setForeground(this.getBackground()); - + noColour.setBackground(this.getBackground()); + noColour.setForeground(this.getBackground()); } else { maxColour.setEnabled(true); minColour.setEnabled(true); + noColour.setEnabled(true); maxColour.setBackground(oldmaxColour); - minColour.setBackground(oldminColour); maxColour.setForeground(oldmaxColour); + minColour.setBackground(oldminColour); minColour.setForeground(oldminColour); + noColour.setBackground(oldNoColour); + noColour.setForeground(oldNoColour); } + + /* + * save the colour, and repaint stuff + */ fr.setColour(type, acg); cs = acg; - ap.paintAlignment(updateOverview); + ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview); } @Override @@ -539,19 +858,11 @@ public class FeatureColourChooser extends JalviewDialog void reset() { fr.setColour(type, oldcs); - ap.paintAlignment(true); + ap.paintAlignment(true, true); cs = null; } /** - * Action on change of choice of No / Above / Below Threshold - */ - protected void threshold_actionPerformed() - { - changeColour(true); - } - - /** * Action on text entry of a threshold value */ protected void thresholdValue_actionPerformed() @@ -565,7 +876,7 @@ public class FeatureColourChooser extends JalviewDialog /* * force repaint of any Overview window or structure */ - ap.paintAlignment(true); + ap.paintAlignment(true, true); } catch (NumberFormatException ex) { } @@ -594,16 +905,6 @@ public class FeatureColourChooser extends JalviewDialog changeColour(false); } - protected void thresholdIsMin_actionPerformed() - { - changeColour(true); - } - - protected void colourByLabel_actionPerformed() - { - changeColour(true); - } - void addActionListener(ActionListener graduatedColorEditor) { if (colourEditor != null) @@ -629,4 +930,82 @@ public class FeatureColourChooser extends JalviewDialog return cs; } + /** + * 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 be restricted to attributes for which we + * hold a range of numerical values (so suitable candidates for a graduated + * colour scheme). + * + * @param featureType + * @param attNames + * @param withNumericRange + */ + protected JComboBox populateAttributesDropdown( + String featureType, List attNames, + boolean withNumericRange) + { + 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(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() + { + @Override + public void itemStateChanged(ItemEvent e) + { + setAttributeMinMax(attCombo.getSelectedItem().toString()); + changeColour(true); + } + }); + + if (validAtts.isEmpty()) + { + attCombo.setToolTipText(MessageManager + .getString(withNumericRange ? "label.no_numeric_attributes" + : "label.no_attributes")); + } + + return attCombo; + } + + /** + * Updates the min-max range and scale to be that for the given attribute name + * + * @param attributeName + */ + protected void setAttributeMinMax(String attributeName) + { + float[] minMax = FeatureAttributes.getInstance().getMinMax(type, + attributeName); + if (minMax != null) + { + min = minMax[0]; + max = minMax[1]; + scaleFactor = (max == min) ? 1f : 100f / (max - min); + } + } + }