/*
- * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
- * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * This file is part of Jalview.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.gui;
-import java.util.*;
+import jalview.api.FeatureColourI;
+import jalview.datamodel.GraphLine;
+import jalview.datamodel.features.FeatureAttributes;
+import jalview.schemes.FeatureColour;
+import jalview.util.MessageManager;
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.event.*;
-
-import jalview.datamodel.*;
-import jalview.schemes.*;
+import java.awt.Color;
import java.awt.Dimension;
-
-public class FeatureColourChooser extends JPanel
+import java.awt.FlowLayout;
+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.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JSlider;
+import javax.swing.JTextField;
+import javax.swing.border.LineBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+public class FeatureColourChooser extends JalviewDialog
{
- JInternalFrame frame;
-
- FeatureRenderer fr;
+ private static final String COLON = ":";
+
+ private static final int MAX_TOOLTIP_LENGTH = 50;
+
+ private static int NO_COLOUR_OPTION = 0;
+
+ private static int MIN_COLOUR_OPTION = 1;
- FeatureSettings fs;
+ private static int MAX_COLOUR_OPTION = 2;
- GraduatedColor cs;
- Object oldcs;
+ private FeatureRenderer fr;
- Hashtable oldgroupColours;
-
- AlignmentPanel ap;
-
+ private FeatureColourI cs;
- boolean adjusting = false;
+ private FeatureColourI oldcs;
+
+ private AlignmentPanel ap;
+
+ private boolean adjusting = false;
private float min;
private float max;
- String type = null;
- public FeatureColourChooser(FeatureSettings fsettings, String type)
+
+ private float scaleFactor;
+
+ private String type = null;
+
+ private JPanel minColour = new JPanel();
+
+ private JPanel maxColour = new JPanel();
+
+ private Color noColour;
+
+ private JComboBox<String> threshold = new JComboBox<>();
+
+ private JSlider slider = new JSlider();
+
+ private JTextField thresholdValue = new JTextField(20);
+
+ private JCheckBox thresholdIsMin = 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;
+
+ private ActionListener changeMinMaxAction;
+
+ /*
+ * choice of option for 'colour for no value'
+ */
+ private JComboBox<String> noValueCombo;
+
+ /*
+ * choice of attribute (if any) for 'colour by text'
+ */
+ private JComboBox<String> textAttributeCombo;
+
+ /*
+ * choice of attribute (if any) for 'colour by value'
+ */
+ private JComboBox<String> valueAttributeCombo;
+
+ /**
+ * Constructor
+ *
+ * @param frender
+ * @param theType
+ */
+ public FeatureColourChooser(FeatureRenderer frender, String theType)
{
- this.fs = fsettings;
- this.type = type;
- fr = fsettings.fr;
+ this(frender, false, theType);
+ }
+
+ /**
+ * Constructor, with option to make a blocking dialog (has to complete in the
+ * AWT event queue thread). Currently this option is always set to false.
+ *
+ * @param frender
+ * @param blocking
+ * @param theType
+ */
+ FeatureColourChooser(FeatureRenderer frender, boolean blocking,
+ String theType)
+ {
+ this.fr = frender;
+ this.type = theType;
ap = fr.ap;
- frame = new JInternalFrame();
- frame.setContentPane(this);
- frame.setLayer(JLayeredPane.PALETTE_LAYER);
- Desktop.addInternalFrame(frame, "Graduated Feature Colour for "+type, 480, 145);
+ String title = MessageManager.formatMessage("label.variable_color_for",
+ new String[] { theType });
+ initDialogFrame(this, true, blocking, title, 470, 300);
slider.addChangeListener(new ChangeListener()
{
+ @Override
public void stateChanged(ChangeEvent evt)
{
if (!adjusting)
{
- thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
- valueChanged();
+ thresholdValue.setText((slider.getValue() / scaleFactor) + "");
+ sliderValueChanged();
}
}
});
slider.addMouseListener(new MouseAdapter()
{
+ @Override
public void mouseReleased(MouseEvent evt)
{
- if (fr.ap!=null) { fr.ap.paintAlignment(true); };
+ /*
+ * only update Overview and/or structure colouring
+ * when threshold slider drag ends (mouse up)
+ */
+ if (ap != null)
+ {
+ ap.paintAlignment(true, true);
+ }
}
});
- float mm[] = ((float[][]) fr.minmax.get(type))[0];
+ // todo move all threshold setup inside a method
+ float mm[] = fr.getMinMax().get(theType)[0];
min = mm[0];
max = mm[1];
- oldcs = fr.featureColours.get(type);
- if (oldcs instanceof GraduatedColor)
+
+ /*
+ * ensure scale factor allows a scaled range with
+ * 10 integer divisions ('ticks'); if we have got here,
+ * we should expect that max != min
+ */
+ scaleFactor = (max == min) ? 1f : 100f / (max - min);
+
+ oldcs = fr.getFeatureColours().get(theType);
+ if (!oldcs.isSimpleColour())
{
- if (((GraduatedColor)oldcs).isAutoScale())
+ if (oldcs.isAutoScaled())
{
// update the scale
- cs = new GraduatedColor((GraduatedColor) oldcs, min, max);
- } else {
- cs = new GraduatedColor((GraduatedColor) oldcs);
+ cs = new FeatureColour((FeatureColour) oldcs, min, max);
+ }
+ else
+ {
+ cs = new FeatureColour((FeatureColour) oldcs);
}
- } else {
- // promote original color to a graduated color
- Color bl = Color.black;
- if (oldcs instanceof Color)
+ }
+ else
+ {
+ /*
+ * 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)
{
- bl = (Color) oldcs;
+ bl = Color.BLACK;
}
// original colour becomes the maximum colour
- cs = new GraduatedColor(Color.white,bl,mm[0],mm[1]);
- cs.setColourByLabel(false);
+ cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
+ cs.setColourByLabel(mm[0] == mm[1]);
}
- minColour.setBackground(cs.getMinColor());
- maxColour.setBackground(cs.getMaxColor());
+ minColour.setBackground(oldminColour = cs.getMinColour());
+ maxColour.setBackground(oldmaxColour = cs.getMaxColour());
+ noColour = cs.getNoColour();
+
adjusting = true;
-
+
try
{
jbInit();
} catch (Exception ex)
{
+ ex.printStackTrace();
+ return;
}
- // update the gui from threshold state
- thresholdIsMin.setSelected(!cs.isAutoScale());
- colourByLabel.setSelected(cs.isColourByLabel());
- if (cs.getThreshType()!=AnnotationColourGradient.NO_THRESHOLD)
+
+ /*
+ * set the initial state of options on screen
+ */
+ if (cs.isColourByLabel())
+ {
+ if (cs.isColourByAttribute())
+ {
+ byAttributeText.setSelected(true);
+ textAttributeCombo.setEnabled(true);
+ String[] attributeName = cs.getAttributeName();
+ textAttributeCombo
+ .setSelectedItem(String.join(COLON, attributeName));
+ }
+ else
+ {
+ byDescription.setSelected(true);
+ textAttributeCombo.setEnabled(false);
+ }
+ }
+ else
+ {
+ if (cs.isColourByAttribute())
+ {
+ byAttributeValue.setSelected(true);
+ String[] attributeName = cs.getAttributeName();
+ valueAttributeCombo
+ .setSelectedItem(String.join(COLON, attributeName));
+ valueAttributeCombo.setEnabled(true);
+ updateMinMax();
+ }
+ else
+ {
+ byScore.setSelected(true);
+ valueAttributeCombo.setEnabled(false);
+ }
+ }
+
+ if (noColour == null)
+ {
+ noValueCombo.setSelectedIndex(NO_COLOUR_OPTION);
+ }
+ else if (noColour.equals(oldminColour))
+ {
+ noValueCombo.setSelectedIndex(MIN_COLOUR_OPTION);
+ }
+ else if (noColour.equals(oldmaxColour))
+ {
+ noValueCombo.setSelectedIndex(MAX_COLOUR_OPTION);
+ }
+
+ threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black);
+ threshline.value = cs.getThreshold();
+
+ if (cs.hasThreshold())
{
// initialise threshold slider and selector
- threshold.setSelectedIndex(cs.getThreshType()==AnnotationColourGradient.ABOVE_THRESHOLD ? 1 : 2);
- slider.setEnabled(true);
+ threshold.setSelectedIndex(cs.isAboveThreshold() ? 1 : 2);
+ slider.setEnabled(true);
+ slider.setValue((int) (cs.getThreshold() * scaleFactor));
thresholdValue.setEnabled(true);
- threshline = new jalview.datamodel.GraphLine(
- (max - min) / 2f,
- "Threshold", Color.black);
-
}
adjusting = false;
- changeColour();
+ changeColour(false);
+ waitForInput();
+ }
+
+ /**
+ * 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);
+ }
+ };
+ changeMinMaxAction = new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ updateMinMax();
+ 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);
}
- public FeatureColourChooser()
+ /**
+ * Updates the min-max range for a change in choice of Colour by Score, or
+ * Colour by Attribute (value)
+ */
+ protected void updateMinMax()
{
- try
+ float[] minMax = null;
+ if (byScore.isSelected())
{
- jbInit();
- } catch (Exception ex)
+ minMax = fr.getMinMax().get(type)[0];
+ }
+ else if (byAttributeValue.isSelected())
{
- ex.printStackTrace();
+ String attName = (String) valueAttributeCombo.getSelectedItem();
+ String[] attNames = attName.split(COLON);
+ minMax = FeatureAttributes.getInstance().getMinMax(type, attNames);
+ }
+ if (minMax != null)
+ {
+ min = minMax[0];
+ max = minMax[1];
+ scaleFactor = (max == min) ? 1f : 100f / (max - min);
+ slider.setValue((int) (min * scaleFactor));
}
}
- private void jbInit() throws Exception
+ /**
+ * Lay out fields for graduated colour by value
+ *
+ * @return
+ */
+ protected JPanel initColourByValuePanel()
{
- minColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
- minColour.setBorder(BorderFactory.createEtchedBorder());
+ JPanel byValuePanel = new JPanel();
+ byValuePanel.setLayout(new BoxLayout(byValuePanel, BoxLayout.Y_AXIS));
+ JvSwingUtils.createItalicTitledBorder(byValuePanel,
+ MessageManager.getString("label.colour_by_value"), true);
+ 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(changeMinMaxAction);
+
+ byAttributeValue.setText(MessageManager.getString("label.attribute"));
+ byAttributeValue.addActionListener(changeMinMaxAction);
+ byWhatPanel.add(byAttributeValue);
+
+ List<String[]> 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));
minColour.setPreferredSize(new Dimension(40, 20));
- minColour.setToolTipText("Minimum Colour");
+ minColour.setToolTipText(MessageManager.getString("label.min_colour"));
minColour.addMouseListener(new MouseAdapter()
{
+ @Override
public void mousePressed(MouseEvent e)
{
if (minColour.isEnabled())
}
}
});
- maxColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
- maxColour.setBorder(BorderFactory.createEtchedBorder());
+
+ maxColour.setFont(JvSwingUtils.getLabelFont());
+ maxColour.setBorder(BorderFactory.createLineBorder(Color.black));
maxColour.setPreferredSize(new Dimension(40, 20));
- maxColour.setToolTipText("Maximum Colour");
+ maxColour.setToolTipText(MessageManager.getString("label.max_colour"));
maxColour.addMouseListener(new MouseAdapter()
{
+ @Override
public void mousePressed(MouseEvent e)
{
if (maxColour.isEnabled())
}
}
});
- ok.setOpaque(false);
- ok.setText("OK");
- ok.addActionListener(new ActionListener()
+ maxColour.setBorder(new LineBorder(Color.black));
+
+ noValueCombo = new JComboBox<>();
+ noValueCombo.addItem(MessageManager.getString("label.no_colour"));
+ noValueCombo.addItem(MessageManager.getString("label.min_colour"));
+ noValueCombo.addItem(MessageManager.getString("label.max_colour"));
+ noValueCombo.addItemListener(new ItemListener()
{
- public void actionPerformed(ActionEvent e)
+ @Override
+ public void itemStateChanged(ItemEvent e)
{
- ok_actionPerformed(e);
+ setNoValueColour();
}
});
- cancel.setOpaque(false);
- cancel.setText("Cancel");
- cancel.addActionListener(new ActionListener()
+
+ JLabel minText = new JLabel(MessageManager.getString("label.min_value"));
+ minText.setFont(JvSwingUtils.getLabelFont());
+ JLabel maxText = new JLabel(MessageManager.getString("label.max_value"));
+ maxText.setFont(JvSwingUtils.getLabelFont());
+ JLabel noText = new JLabel(MessageManager.getString("label.no_value"));
+ noText.setFont(JvSwingUtils.getLabelFont());
+
+ colourRangePanel.add(minText);
+ colourRangePanel.add(minColour);
+ colourRangePanel.add(maxText);
+ colourRangePanel.add(maxColour);
+ colourRangePanel.add(noText);
+ colourRangePanel.add(noValueCombo);
+
+ /*
+ * 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
+ .getString("label.threshold_feature_no_threshold")); // index 0
+ threshold.addItem(MessageManager
+ .getString("label.threshold_feature_above_threshold")); // index 1
+ threshold.addItem(MessageManager
+ .getString("label.threshold_feature_below_threshold")); // index 2
+
+ thresholdValue.addActionListener(new ActionListener()
{
+ @Override
public void actionPerformed(ActionEvent e)
{
- cancel_actionPerformed(e);
+ thresholdValue_actionPerformed();
}
});
- this.setLayout(borderLayout1);
- jPanel2.setLayout(flowLayout1);
- jPanel1.setBackground(Color.white);
- jPanel2.setBackground(Color.white);
- threshold.addActionListener(new ActionListener()
+ thresholdValue.addFocusListener(new FocusAdapter()
{
- public void actionPerformed(ActionEvent e)
+ @Override
+ public void focusLost(FocusEvent e)
{
- threshold_actionPerformed(e);
- }
- });
- threshold.addItem("No Threshold"); // index 0
- threshold.addItem("Above Threshold"); // index 1
- threshold.addItem("Below Threshold"); // index 2
- jPanel3.setLayout(flowLayout2);
- thresholdValue.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- thresholdValue_actionPerformed(e);
+ thresholdValue_actionPerformed();
}
});
slider.setPaintLabels(false);
slider.setEnabled(false);
slider.setOpaque(false);
slider.setPreferredSize(new Dimension(100, 32));
+ slider.setToolTipText(MessageManager
+ .getString("label.adjust_threshold"));
thresholdValue.setEnabled(false);
thresholdValue.setColumns(7);
- jPanel3.setBackground(Color.white);
- currentColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
- currentColours.setOpaque(false);
- currentColours.setText("Use Original Colours");
- currentColours.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- currentColours_actionPerformed(e);
- }
- });
+
+ 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("Threshold is Min/Max");
- thresholdIsMin.addActionListener(new ActionListener()
+ thresholdIsMin.setText(MessageManager
+ .getString("label.threshold_minmax"));
+ thresholdIsMin.setToolTipText(MessageManager
+ .getString("label.toggle_absolute_relative_display_threshold"));
+ thresholdIsMin.addActionListener(changeColourAction);
+ isMinMaxPanel.add(thresholdIsMin);
+
+ return byValuePanel;
+ }
+
+ /**
+ * Action on user choice of no / min / max colour to use when there is no
+ * value to colour by
+ */
+ protected void setNoValueColour()
+ {
+ int i = noValueCombo.getSelectedIndex();
+ if (i == NO_COLOUR_OPTION)
{
- public void actionPerformed(ActionEvent actionEvent)
- {
- thresholdIsMin_actionPerformed(actionEvent);
- }
- });
- colourByLabel.setBackground(Color.white);
- colourByLabel.setText("Colour by Label");
- colourByLabel.addActionListener(new ActionListener()
+ noColour = null;
+ }
+ else if (i == MIN_COLOUR_OPTION)
{
- public void actionPerformed(ActionEvent actionEvent)
- {
- colourByLabel_actionPerformed(actionEvent);
- }
- });
- jPanel1.add(ok);
- jPanel1.add(cancel);
- // jPanel2.add(currentColours);
- jPanel2.add(minColour);
- jPanel2.add(maxColour);
- this.add(jPanel3, java.awt.BorderLayout.CENTER);
- jPanel3.add(threshold);
- jPanel3.add(slider);
- jPanel3.add(thresholdValue);
- jPanel3.add(thresholdIsMin);
- jPanel3.add(colourByLabel);
- this.add(jPanel1, java.awt.BorderLayout.SOUTH);
- this.add(jPanel2, java.awt.BorderLayout.NORTH);
+ noColour = minColour.getBackground();
+ }
+ else if (i == MAX_COLOUR_OPTION)
+ {
+ noColour = maxColour.getBackground();
+ }
+ changeColour(true);
}
+ /**
+ * 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;
+ }
- JPanel minColour = new JPanel();
-
- JPanel maxColour = new JPanel();
-
- JButton ok = new JButton();
-
- JButton cancel = new JButton();
-
- JPanel jPanel1 = new JPanel();
-
- JPanel jPanel2 = new JPanel();
-
- BorderLayout borderLayout1 = new BorderLayout();
-
- JComboBox threshold = new JComboBox();
-
- FlowLayout flowLayout1 = new FlowLayout();
-
- JPanel jPanel3 = new JPanel();
-
- FlowLayout flowLayout2 = new FlowLayout();
-
- JSlider slider = new JSlider();
-
- JTextField thresholdValue = new JTextField(20);
- // TODO refactor to tolower flag
- JCheckBox currentColours = new JCheckBox();
-
- JCheckBox thresholdIsMin = new JCheckBox();
- JCheckBox colourByLabel = new JCheckBox();
+ /**
+ * 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);
+ JvSwingUtils.createItalicTitledBorder(byTextPanel,
+ MessageManager.getString("label.colour_by_text"), true);
+
+ 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<String[]> 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 GraphLine threshline;
+ 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)
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 = new GraduatedColor(minColour.getBackground(), maxColour.getBackground(), min, max);
- if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
+ /*
+ * make the feature colour
+ */
+ FeatureColourI acg;
+ if (cs.isColourByLabel())
+ {
+ acg = new FeatureColour(oldminColour, oldmaxColour, min, max);
+ }
+ else
+ {
+ 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 (!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(!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)
- {
- acg = new GraduatedColor(acg, threshline.value, max);
- } else {
- acg = new GraduatedColor(acg, min,threshline.value);
+ if (aboveThreshold)
+ {
+ acg = new FeatureColour((FeatureColour) acg, threshline.value, max);
+ }
+ else
+ {
+ acg = new FeatureColour((FeatureColour) acg, min, threshline.value);
}
- } else {
- acg.setAutoScaled(true);
}
- acg.setColourByLabel(colourByLabel.isSelected());
- fr.featureColours.put(type,acg);
- cs = acg;
- ap.paintAlignment(false);
- }
-
- public void ok_actionPerformed(ActionEvent e)
- {
- changeColour();
- try
- {
- frame.setClosed(true);
- } catch (Exception ex)
+ else
{
+ acg.setAutoScaled(true);
}
+ 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);
+ maxColour.setForeground(oldmaxColour);
+ minColour.setBackground(oldminColour);
+ minColour.setForeground(oldminColour);
+ noColour = oldNoColour;
+ }
+
+ /*
+ * save the colour, and repaint stuff
+ */
+ fr.setColour(type, acg);
+ cs = acg;
+ ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
}
- public void cancel_actionPerformed(ActionEvent e)
+ @Override
+ protected void raiseClosed()
{
- reset();
- try
- {
- frame.setClosed(true);
- } catch (Exception ex)
+ if (this.colourEditor != null)
{
+ colourEditor.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
}
}
- void reset()
- {
- fr.featureColours.put(type, oldcs);
- }
-
- public void thresholdCheck_actionPerformed(ActionEvent e)
+ @Override
+ public void okPressed()
{
- changeColour();
+ changeColour(false);
}
- public void annotations_actionPerformed(ActionEvent e)
+ @Override
+ public void cancelPressed()
{
- changeColour();
+ reset();
}
- public void threshold_actionPerformed(ActionEvent e)
+ /**
+ * 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()
{
- changeColour();
+ fr.setColour(type, oldcs);
+ ap.paintAlignment(true, true);
+ cs = null;
}
- 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 currentColours_actionPerformed(ActionEvent e)
+ void addActionListener(ActionListener graduatedColorEditor)
{
- if (currentColours.isSelected())
+ if (colourEditor != null)
{
- reset();
+ System.err.println(
+ "IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
}
-
- maxColour.setEnabled(!currentColours.isSelected());
- minColour.setEnabled(!currentColours.isSelected());
-
- changeColour();
+ colourEditor = graduatedColorEditor;
}
- public void thresholdIsMin_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;
}
- public void colourByLabel_actionPerformed(ActionEvent actionEvent)
+
+ /**
+ * 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).
+ * <p>
+ * 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<String> populateAttributesDropdown(
+ String featureType, List<String[]> attNames,
+ boolean withNumericRange)
{
- changeColour();
+ List<String> validAtts = new ArrayList<>();
+ List<String> 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<String> attCombo = JvSwingUtils.buildComboWithTooltips(
+ validAtts, tooltips);
+
+ attCombo.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ changeMinMaxAction.actionPerformed(null);
+ }
+ });
+
+ if (validAtts.isEmpty())
+ {
+ attCombo.setToolTipText(MessageManager
+ .getString(withNumericRange ? "label.no_numeric_attributes"
+ : "label.no_attributes"));
+ }
+
+ return attCombo;
}
}