2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.FeatureColourI;
24 import jalview.datamodel.GraphLine;
25 import jalview.schemes.FeatureColour;
26 import jalview.util.MessageManager;
28 import java.awt.BorderLayout;
29 import java.awt.Color;
30 import java.awt.Dimension;
31 import java.awt.FlowLayout;
32 import java.awt.GridLayout;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.FocusAdapter;
36 import java.awt.event.FocusEvent;
37 import java.awt.event.MouseAdapter;
38 import java.awt.event.MouseEvent;
40 import javax.swing.BorderFactory;
41 import javax.swing.JCheckBox;
42 import javax.swing.JColorChooser;
43 import javax.swing.JComboBox;
44 import javax.swing.JLabel;
45 import javax.swing.JPanel;
46 import javax.swing.JSlider;
47 import javax.swing.JTextField;
48 import javax.swing.border.LineBorder;
49 import javax.swing.event.ChangeEvent;
50 import javax.swing.event.ChangeListener;
52 public class FeatureColourChooser extends JalviewDialog
54 // FeatureSettings fs;
55 private FeatureRenderer fr;
57 private FeatureColourI cs;
59 private FeatureColourI oldcs;
61 private AlignmentPanel ap;
63 private boolean adjusting = false;
65 final private float min;
67 final private float max;
69 final private float scaleFactor;
71 private String type = null;
73 private JPanel minColour = new JPanel();
75 private JPanel maxColour = new JPanel();
77 private JComboBox<String> threshold = new JComboBox<>();
79 private JSlider slider = new JSlider();
81 private JTextField thresholdValue = new JTextField(20);
83 // TODO implement GUI for tolower flag
84 // JCheckBox toLower = new JCheckBox();
86 private JCheckBox thresholdIsMin = new JCheckBox();
88 private JCheckBox colourByLabel = new JCheckBox();
90 private GraphLine threshline;
92 private Color oldmaxColour;
94 private Color oldminColour;
96 private ActionListener colourEditor = null;
104 public FeatureColourChooser(FeatureRenderer frender, String theType)
106 this(frender, false, theType);
110 * Constructor, with option to make a blocking dialog (has to complete in the
111 * AWT event queue thread). Currently this option is always set to false.
117 FeatureColourChooser(FeatureRenderer frender, boolean blocking,
123 String title = MessageManager
124 .formatMessage("label.graduated_color_for_params", new String[]
126 initDialogFrame(this, true, blocking, title, 480, 185);
128 slider.addChangeListener(new ChangeListener()
131 public void stateChanged(ChangeEvent evt)
135 thresholdValue.setText((slider.getValue() / scaleFactor) + "");
136 sliderValueChanged();
140 slider.addMouseListener(new MouseAdapter()
143 public void mouseReleased(MouseEvent evt)
146 * only update Overview and/or structure colouring
147 * when threshold slider drag ends (mouse up)
151 ap.paintAlignment(true, true);
156 float mm[] = fr.getMinMax().get(theType)[0];
161 * ensure scale factor allows a scaled range with
162 * 10 integer divisions ('ticks'); if we have got here,
163 * we should expect that max != min
165 scaleFactor = (max == min) ? 1f : 100f / (max - min);
167 oldcs = fr.getFeatureColours().get(theType);
168 if (!oldcs.isSimpleColour())
170 if (oldcs.isAutoScaled())
173 cs = new FeatureColour((FeatureColour) oldcs, min, max);
177 cs = new FeatureColour((FeatureColour) oldcs);
182 // promote original color to a graduated color
183 Color bl = oldcs.getColour();
188 // original colour becomes the maximum colour
189 cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
190 cs.setColourByLabel(false);
192 minColour.setBackground(oldminColour = cs.getMinColour());
193 maxColour.setBackground(oldmaxColour = cs.getMaxColour());
199 } catch (Exception ex)
202 // update the gui from threshold state
203 thresholdIsMin.setSelected(!cs.isAutoScaled());
204 colourByLabel.setSelected(cs.isColourByLabel());
205 if (cs.hasThreshold())
207 // initialise threshold slider and selector
208 threshold.setSelectedIndex(cs.isAboveThreshold() ? 1 : 2);
209 slider.setEnabled(true);
210 slider.setValue((int) (cs.getThreshold() * scaleFactor));
211 thresholdValue.setEnabled(true);
212 threshline = new GraphLine((max - min) / 2f, "Threshold",
214 threshline.value = cs.getThreshold();
223 private void jbInit() throws Exception
225 this.setLayout(new GridLayout(4, 1));
227 JPanel colourByPanel = initColoursPanel();
229 JPanel thresholdPanel = initThresholdPanel();
231 JPanel okCancelPanel = initOkCancelPanel();
233 this.add(colourByPanel);
234 this.add(thresholdPanel);
236 this.add(okCancelPanel);
240 * Lay out fields for threshold options
244 protected JPanel initThresholdPanel()
246 JPanel thresholdPanel = new JPanel();
247 thresholdPanel.setLayout(new FlowLayout());
248 threshold.addActionListener(new ActionListener()
251 public void actionPerformed(ActionEvent e)
256 threshold.setToolTipText(MessageManager
257 .getString("label.threshold_feature_display_by_score"));
258 threshold.addItem(MessageManager
259 .getString("label.threshold_feature_no_threshold")); // index 0
260 threshold.addItem(MessageManager
261 .getString("label.threshold_feature_above_threshold")); // index 1
262 threshold.addItem(MessageManager
263 .getString("label.threshold_feature_below_threshold")); // index 2
265 thresholdValue.addActionListener(new ActionListener()
268 public void actionPerformed(ActionEvent e)
270 thresholdValue_actionPerformed();
273 thresholdValue.addFocusListener(new FocusAdapter()
276 public void focusLost(FocusEvent e)
278 thresholdValue_actionPerformed();
281 slider.setPaintLabels(false);
282 slider.setPaintTicks(true);
283 slider.setBackground(Color.white);
284 slider.setEnabled(false);
285 slider.setOpaque(false);
286 slider.setPreferredSize(new Dimension(100, 32));
287 slider.setToolTipText(
288 MessageManager.getString("label.adjust_threshold"));
289 thresholdValue.setEnabled(false);
290 thresholdValue.setColumns(7);
291 thresholdPanel.setBackground(Color.white);
292 thresholdIsMin.setBackground(Color.white);
294 .setText(MessageManager.getString("label.threshold_minmax"));
295 thresholdIsMin.setToolTipText(MessageManager
296 .getString("label.toggle_absolute_relative_display_threshold"));
297 thresholdIsMin.addActionListener(new ActionListener()
300 public void actionPerformed(ActionEvent actionEvent)
305 thresholdPanel.add(threshold);
306 thresholdPanel.add(slider);
307 thresholdPanel.add(thresholdValue);
308 thresholdPanel.add(thresholdIsMin);
309 return thresholdPanel;
313 * Lay out OK and Cancel buttons
317 protected JPanel initOkCancelPanel()
319 JPanel okCancelPanel = new JPanel();
320 okCancelPanel.setBackground(Color.white);
321 okCancelPanel.add(ok);
322 okCancelPanel.add(cancel);
323 return okCancelPanel;
327 * Lay out Colour by Label and min/max colour widgets
331 protected JPanel initColoursPanel()
333 JPanel colourByPanel = new JPanel();
334 colourByPanel.setLayout(new FlowLayout());
335 colourByPanel.setBackground(Color.white);
336 minColour.setFont(JvSwingUtils.getLabelFont());
337 minColour.setBorder(BorderFactory.createLineBorder(Color.black));
338 minColour.setPreferredSize(new Dimension(40, 20));
339 minColour.setToolTipText(MessageManager.getString("label.min_colour"));
340 minColour.addMouseListener(new MouseAdapter()
343 public void mousePressed(MouseEvent e)
345 if (minColour.isEnabled())
347 minColour_actionPerformed();
351 maxColour.setFont(JvSwingUtils.getLabelFont());
352 maxColour.setBorder(BorderFactory.createLineBorder(Color.black));
353 maxColour.setPreferredSize(new Dimension(40, 20));
354 maxColour.setToolTipText(MessageManager.getString("label.max_colour"));
355 maxColour.addMouseListener(new MouseAdapter()
358 public void mousePressed(MouseEvent e)
360 if (maxColour.isEnabled())
362 maxColour_actionPerformed();
366 maxColour.setBorder(new LineBorder(Color.black));
367 JLabel minText = new JLabel(MessageManager.getString("label.min"));
368 minText.setFont(JvSwingUtils.getLabelFont());
369 JLabel maxText = new JLabel(MessageManager.getString("label.max"));
370 maxText.setFont(JvSwingUtils.getLabelFont());
372 JPanel colourPanel = new JPanel();
373 colourPanel.setBackground(Color.white);
374 colourPanel.add(minText);
375 colourPanel.add(minColour);
376 colourPanel.add(maxText);
377 colourPanel.add(maxColour);
378 colourByPanel.add(colourByLabel, BorderLayout.WEST);
379 colourByPanel.add(colourPanel, BorderLayout.EAST);
381 colourByLabel.setBackground(Color.white);
383 .setText(MessageManager.getString("label.colour_by_label"));
385 .setToolTipText(MessageManager
386 .getString("label.display_features_same_type_different_label_using_different_colour"));
387 colourByLabel.addActionListener(new ActionListener()
390 public void actionPerformed(ActionEvent actionEvent)
396 return colourByPanel;
400 * Action on clicking the 'minimum colour' - open a colour chooser dialog, and
401 * set the selected colour (if the user does not cancel out of the dialog)
403 protected void minColour_actionPerformed()
405 Color col = JColorChooser.showDialog(this,
406 MessageManager.getString("label.select_colour_minimum_value"),
407 minColour.getBackground());
410 minColour.setBackground(col);
411 minColour.setForeground(col);
418 * Action on clicking the 'maximum colour' - open a colour chooser dialog, and
419 * set the selected colour (if the user does not cancel out of the dialog)
421 protected void maxColour_actionPerformed()
423 Color col = JColorChooser.showDialog(this,
424 MessageManager.getString("label.select_colour_maximum_value"),
425 maxColour.getBackground());
428 maxColour.setBackground(col);
429 maxColour.setForeground(col);
436 * Constructs and sets the selected colour options as the colour for the
437 * feature type, and repaints the alignment, and optionally the Overview
438 * and/or structure viewer if open
440 * @param updateStructsAndOverview
442 void changeColour(boolean updateStructsAndOverview)
444 // Check if combobox is still adjusting
450 boolean aboveThreshold = false;
451 boolean belowThreshold = false;
452 if (threshold.getSelectedIndex() == 1)
454 aboveThreshold = true;
456 else if (threshold.getSelectedIndex() == 2)
458 belowThreshold = true;
460 boolean hasThreshold = aboveThreshold || belowThreshold;
462 slider.setEnabled(true);
463 thresholdValue.setEnabled(true);
466 if (cs.isColourByLabel())
468 acg = new FeatureColour(oldminColour, oldmaxColour, min, max);
472 acg = new FeatureColour(oldminColour = minColour.getBackground(),
473 oldmaxColour = maxColour.getBackground(), min, max);
478 slider.setEnabled(false);
479 thresholdValue.setEnabled(false);
480 thresholdValue.setText("");
481 thresholdIsMin.setEnabled(false);
483 else if (threshline == null)
486 * todo not yet implemented: visual indication of feature threshold
488 threshline = new GraphLine((max - min) / 2f, "Threshold",
495 acg.setThreshold(threshline.value);
497 float range = (max - min) * scaleFactor;
499 slider.setMinimum((int) (min * scaleFactor));
500 slider.setMaximum((int) (max * scaleFactor));
501 // slider.setValue((int) (threshline.value * scaleFactor));
502 slider.setValue(Math.round(threshline.value * scaleFactor));
503 thresholdValue.setText(threshline.value + "");
504 slider.setMajorTickSpacing((int) (range / 10f));
505 slider.setEnabled(true);
506 thresholdValue.setEnabled(true);
507 thresholdIsMin.setEnabled(!colourByLabel.isSelected());
511 acg.setAboveThreshold(aboveThreshold);
512 acg.setBelowThreshold(belowThreshold);
513 if (thresholdIsMin.isSelected() && hasThreshold)
515 acg.setAutoScaled(false);
518 acg = new FeatureColour((FeatureColour) acg, threshline.value, max);
522 acg = new FeatureColour((FeatureColour) acg, min, threshline.value);
527 acg.setAutoScaled(true);
529 acg.setColourByLabel(colourByLabel.isSelected());
530 if (acg.isColourByLabel())
532 maxColour.setEnabled(false);
533 minColour.setEnabled(false);
534 maxColour.setBackground(this.getBackground());
535 maxColour.setForeground(this.getBackground());
536 minColour.setBackground(this.getBackground());
537 minColour.setForeground(this.getBackground());
542 maxColour.setEnabled(true);
543 minColour.setEnabled(true);
544 maxColour.setBackground(oldmaxColour);
545 minColour.setBackground(oldminColour);
546 maxColour.setForeground(oldmaxColour);
547 minColour.setForeground(oldminColour);
550 fr.setColour(type, acg);
552 ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
556 protected void raiseClosed()
558 if (this.colourEditor != null)
560 colourEditor.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
565 public void okPressed()
571 public void cancelPressed()
577 * Action when the user cancels the dialog. All previous settings should be
578 * restored and rendered on the alignment, and any linked Overview window or
583 fr.setColour(type, oldcs);
584 ap.paintAlignment(true, true);
589 * Action on text entry of a threshold value
591 protected void thresholdValue_actionPerformed()
595 float f = Float.parseFloat(thresholdValue.getText());
596 slider.setValue((int) (f * scaleFactor));
597 threshline.value = f;
600 * force repaint of any Overview window or structure
602 ap.paintAlignment(true, true);
603 } catch (NumberFormatException ex)
609 * Action on change of threshold slider value. This may be done interactively
610 * (by moving the slider), or programmatically (to update the slider after
611 * manual input of a threshold value).
613 protected void sliderValueChanged()
616 * squash rounding errors by forcing min/max of slider to
617 * actual min/max of feature score range
619 int value = slider.getValue();
620 threshline.value = value == slider.getMaximum() ? max
621 : (value == slider.getMinimum() ? min : value / scaleFactor);
622 cs.setThreshold(threshline.value);
625 * repaint alignment, but not Overview or structure,
626 * to avoid overload while dragging the slider
631 void addActionListener(ActionListener graduatedColorEditor)
633 if (colourEditor != null)
636 "IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
638 colourEditor = graduatedColorEditor;
642 * Answers the last colour setting selected by user - either oldcs (which may
643 * be a java.awt.Color) or the new GraduatedColor
647 FeatureColourI getLastColour()