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.event.ActionEvent;
33 import java.awt.event.ActionListener;
34 import java.awt.event.FocusAdapter;
35 import java.awt.event.FocusEvent;
36 import java.awt.event.MouseAdapter;
37 import java.awt.event.MouseEvent;
39 import javax.swing.BorderFactory;
40 import javax.swing.JCheckBox;
41 import javax.swing.JColorChooser;
42 import javax.swing.JComboBox;
43 import javax.swing.JLabel;
44 import javax.swing.JPanel;
45 import javax.swing.JSlider;
46 import javax.swing.JTextField;
47 import javax.swing.border.LineBorder;
48 import javax.swing.event.ChangeEvent;
49 import javax.swing.event.ChangeListener;
51 public class FeatureColourChooser extends JalviewDialog
53 // FeatureSettings fs;
54 private FeatureRenderer fr;
56 private FeatureColourI cs;
58 private FeatureColourI oldcs;
60 private AlignmentPanel ap;
62 private boolean adjusting = false;
64 final private float min;
66 final private float max;
68 final private float scaleFactor;
70 private String type = null;
72 private JPanel minColour = new JPanel();
74 private JPanel maxColour = new JPanel();
76 private JComboBox<String> threshold = new JComboBox<>();
78 private JSlider slider = new JSlider();
80 private JTextField thresholdValue = new JTextField(20);
82 // TODO implement GUI for tolower flag
83 // JCheckBox toLower = new JCheckBox();
85 private JCheckBox thresholdIsMin = new JCheckBox();
87 private JCheckBox colourByLabel = new JCheckBox();
89 private GraphLine threshline;
91 private Color oldmaxColour;
93 private Color oldminColour;
95 private ActionListener colourEditor = null;
103 public FeatureColourChooser(FeatureRenderer frender, String theType)
105 this(frender, false, theType);
109 * Constructor, with option to make a blocking dialog (has to complete in the
110 * AWT event queue thread). Currently this option is always set to false.
116 FeatureColourChooser(FeatureRenderer frender, boolean blocking,
122 String title = MessageManager
123 .formatMessage("label.graduated_color_for_params", new String[]
125 initDialogFrame(this, true, blocking, title, 480, 185);
127 slider.addChangeListener(new ChangeListener()
130 public void stateChanged(ChangeEvent evt)
134 thresholdValue.setText((slider.getValue() / scaleFactor) + "");
135 sliderValueChanged();
139 slider.addMouseListener(new MouseAdapter()
142 public void mouseReleased(MouseEvent evt)
145 * only update Overview and/or structure colouring
146 * when threshold slider drag ends (mouse up)
150 ap.paintAlignment(true);
155 float mm[] = fr.getMinMax().get(theType)[0];
160 * ensure scale factor allows a scaled range with
161 * 10 integer divisions ('ticks'); if we have got here,
162 * we should expect that max != min
164 scaleFactor = (max == min) ? 1f : 100f / (max - min);
166 oldcs = fr.getFeatureColours().get(theType);
167 if (!oldcs.isSimpleColour())
169 if (oldcs.isAutoScaled())
172 cs = new FeatureColour((FeatureColour) oldcs, min, max);
176 cs = new FeatureColour((FeatureColour) oldcs);
181 // promote original color to a graduated color
182 Color bl = oldcs.getColour();
187 // original colour becomes the maximum colour
188 cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
189 cs.setColourByLabel(false);
191 minColour.setBackground(oldminColour = cs.getMinColour());
192 maxColour.setBackground(oldmaxColour = cs.getMaxColour());
198 } catch (Exception ex)
201 // update the gui from threshold state
202 thresholdIsMin.setSelected(!cs.isAutoScaled());
203 colourByLabel.setSelected(cs.isColourByLabel());
204 if (cs.hasThreshold())
206 // initialise threshold slider and selector
207 threshold.setSelectedIndex(cs.isAboveThreshold() ? 1 : 2);
208 slider.setEnabled(true);
209 slider.setValue((int) (cs.getThreshold() * scaleFactor));
210 thresholdValue.setEnabled(true);
211 threshline = new GraphLine((max - min) / 2f, "Threshold",
213 threshline.value = cs.getThreshold();
222 private void jbInit() throws Exception
225 minColour.setFont(JvSwingUtils.getLabelFont());
226 minColour.setBorder(BorderFactory.createLineBorder(Color.black));
227 minColour.setPreferredSize(new Dimension(40, 20));
228 minColour.setToolTipText(MessageManager.getString("label.min_colour"));
229 minColour.addMouseListener(new MouseAdapter()
232 public void mousePressed(MouseEvent e)
234 if (minColour.isEnabled())
236 minColour_actionPerformed();
240 maxColour.setFont(JvSwingUtils.getLabelFont());
241 maxColour.setBorder(BorderFactory.createLineBorder(Color.black));
242 maxColour.setPreferredSize(new Dimension(40, 20));
243 maxColour.setToolTipText(MessageManager.getString("label.max_colour"));
244 maxColour.addMouseListener(new MouseAdapter()
247 public void mousePressed(MouseEvent e)
249 if (maxColour.isEnabled())
251 maxColour_actionPerformed();
255 maxColour.setBorder(new LineBorder(Color.black));
256 JLabel minText = new JLabel(MessageManager.getString("label.min"));
257 minText.setFont(JvSwingUtils.getLabelFont());
258 JLabel maxText = new JLabel(MessageManager.getString("label.max"));
259 maxText.setFont(JvSwingUtils.getLabelFont());
260 this.setLayout(new BorderLayout());
261 JPanel jPanel1 = new JPanel();
262 jPanel1.setBackground(Color.white);
263 JPanel jPanel2 = new JPanel();
264 jPanel2.setLayout(new FlowLayout());
265 jPanel2.setBackground(Color.white);
266 threshold.addActionListener(new ActionListener()
269 public void actionPerformed(ActionEvent e)
271 threshold_actionPerformed();
274 threshold.setToolTipText(MessageManager
275 .getString("label.threshold_feature_display_by_score"));
276 threshold.addItem(MessageManager
277 .getString("label.threshold_feature_no_threshold")); // index 0
278 threshold.addItem(MessageManager
279 .getString("label.threshold_feature_above_threshold")); // index 1
280 threshold.addItem(MessageManager
281 .getString("label.threshold_feature_below_threshold")); // index 2
283 JPanel jPanel3 = new JPanel();
284 jPanel3.setLayout(new FlowLayout());
285 thresholdValue.addActionListener(new ActionListener()
288 public void actionPerformed(ActionEvent e)
290 thresholdValue_actionPerformed();
293 thresholdValue.addFocusListener(new FocusAdapter()
296 public void focusLost(FocusEvent e)
298 thresholdValue_actionPerformed();
301 slider.setPaintLabels(false);
302 slider.setPaintTicks(true);
303 slider.setBackground(Color.white);
304 slider.setEnabled(false);
305 slider.setOpaque(false);
306 slider.setPreferredSize(new Dimension(100, 32));
307 slider.setToolTipText(
308 MessageManager.getString("label.adjust_threshold"));
309 thresholdValue.setEnabled(false);
310 thresholdValue.setColumns(7);
311 jPanel3.setBackground(Color.white);
312 thresholdIsMin.setBackground(Color.white);
314 .setText(MessageManager.getString("label.threshold_minmax"));
315 thresholdIsMin.setToolTipText(MessageManager
316 .getString("label.toggle_absolute_relative_display_threshold"));
317 thresholdIsMin.addActionListener(new ActionListener()
320 public void actionPerformed(ActionEvent actionEvent)
322 thresholdIsMin_actionPerformed();
325 colourByLabel.setBackground(Color.white);
327 .setText(MessageManager.getString("label.colour_by_label"));
328 colourByLabel.setToolTipText(MessageManager.getString(
329 "label.display_features_same_type_different_label_using_different_colour"));
330 colourByLabel.addActionListener(new ActionListener()
333 public void actionPerformed(ActionEvent actionEvent)
335 colourByLabel_actionPerformed();
339 JPanel colourPanel = new JPanel();
340 colourPanel.setBackground(Color.white);
343 jPanel2.add(colourByLabel, BorderLayout.WEST);
344 jPanel2.add(colourPanel, BorderLayout.EAST);
345 colourPanel.add(minText);
346 colourPanel.add(minColour);
347 colourPanel.add(maxText);
348 colourPanel.add(maxColour);
349 this.add(jPanel3, BorderLayout.CENTER);
350 jPanel3.add(threshold);
352 jPanel3.add(thresholdValue);
353 jPanel3.add(thresholdIsMin);
354 this.add(jPanel1, BorderLayout.SOUTH);
355 this.add(jPanel2, BorderLayout.NORTH);
359 * Action on clicking the 'minimum colour' - open a colour chooser dialog, and
360 * set the selected colour (if the user does not cancel out of the dialog)
362 protected void minColour_actionPerformed()
364 Color col = JColorChooser.showDialog(this,
365 MessageManager.getString("label.select_colour_minimum_value"),
366 minColour.getBackground());
369 minColour.setBackground(col);
370 minColour.setForeground(col);
377 * Action on clicking the 'maximum colour' - open a colour chooser dialog, and
378 * set the selected colour (if the user does not cancel out of the dialog)
380 protected void maxColour_actionPerformed()
382 Color col = JColorChooser.showDialog(this,
383 MessageManager.getString("label.select_colour_maximum_value"),
384 maxColour.getBackground());
387 maxColour.setBackground(col);
388 maxColour.setForeground(col);
395 * Constructs and sets the selected colour options as the colour for the
396 * feature type, and repaints the alignment, and optionally the Overview
397 * and/or structure viewer if open
399 * @param updateOverview
401 void changeColour(boolean updateOverview)
403 // Check if combobox is still adjusting
409 boolean aboveThreshold = false;
410 boolean belowThreshold = false;
411 if (threshold.getSelectedIndex() == 1)
413 aboveThreshold = true;
415 else if (threshold.getSelectedIndex() == 2)
417 belowThreshold = true;
419 boolean hasThreshold = aboveThreshold || belowThreshold;
421 slider.setEnabled(true);
422 thresholdValue.setEnabled(true);
425 if (cs.isColourByLabel())
427 acg = new FeatureColour(oldminColour, oldmaxColour, min, max);
431 acg = new FeatureColour(oldminColour = minColour.getBackground(),
432 oldmaxColour = maxColour.getBackground(), min, max);
437 slider.setEnabled(false);
438 thresholdValue.setEnabled(false);
439 thresholdValue.setText("");
440 thresholdIsMin.setEnabled(false);
442 else if (threshline == null)
445 * todo not yet implemented: visual indication of feature threshold
447 threshline = new GraphLine((max - min) / 2f, "Threshold",
454 acg.setThreshold(threshline.value);
456 float range = (max - min) * scaleFactor;
458 slider.setMinimum((int) (min * scaleFactor));
459 slider.setMaximum((int) (max * scaleFactor));
460 // slider.setValue((int) (threshline.value * scaleFactor));
461 slider.setValue(Math.round(threshline.value * scaleFactor));
462 thresholdValue.setText(threshline.value + "");
463 slider.setMajorTickSpacing((int) (range / 10f));
464 slider.setEnabled(true);
465 thresholdValue.setEnabled(true);
466 thresholdIsMin.setEnabled(!colourByLabel.isSelected());
470 acg.setAboveThreshold(aboveThreshold);
471 acg.setBelowThreshold(belowThreshold);
472 if (thresholdIsMin.isSelected() && hasThreshold)
474 acg.setAutoScaled(false);
477 acg = new FeatureColour((FeatureColour) acg, threshline.value, max);
481 acg = new FeatureColour((FeatureColour) acg, min, threshline.value);
486 acg.setAutoScaled(true);
488 acg.setColourByLabel(colourByLabel.isSelected());
489 if (acg.isColourByLabel())
491 maxColour.setEnabled(false);
492 minColour.setEnabled(false);
493 maxColour.setBackground(this.getBackground());
494 maxColour.setForeground(this.getBackground());
495 minColour.setBackground(this.getBackground());
496 minColour.setForeground(this.getBackground());
501 maxColour.setEnabled(true);
502 minColour.setEnabled(true);
503 maxColour.setBackground(oldmaxColour);
504 minColour.setBackground(oldminColour);
505 maxColour.setForeground(oldmaxColour);
506 minColour.setForeground(oldminColour);
508 fr.setColour(type, acg);
510 ap.paintAlignment(updateOverview);
514 protected void raiseClosed()
516 if (this.colourEditor != null)
518 colourEditor.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
523 public void okPressed()
529 public void cancelPressed()
535 * Action when the user cancels the dialog. All previous settings should be
536 * restored and rendered on the alignment, and any linked Overview window or
541 fr.setColour(type, oldcs);
542 ap.paintAlignment(true);
547 * Action on change of choice of No / Above / Below Threshold
549 protected void threshold_actionPerformed()
555 * Action on text entry of a threshold value
557 protected void thresholdValue_actionPerformed()
561 float f = Float.parseFloat(thresholdValue.getText());
562 slider.setValue((int) (f * scaleFactor));
563 threshline.value = f;
566 * force repaint of any Overview window or structure
568 ap.paintAlignment(true);
569 } catch (NumberFormatException ex)
575 * Action on change of threshold slider value. This may be done interactively
576 * (by moving the slider), or programmatically (to update the slider after
577 * manual input of a threshold value).
579 protected void sliderValueChanged()
582 * squash rounding errors by forcing min/max of slider to
583 * actual min/max of feature score range
585 int value = slider.getValue();
586 threshline.value = value == slider.getMaximum() ? max
587 : (value == slider.getMinimum() ? min : value / scaleFactor);
588 cs.setThreshold(threshline.value);
591 * repaint alignment, but not Overview or structure,
592 * to avoid overload while dragging the slider
597 protected void thresholdIsMin_actionPerformed()
602 protected void colourByLabel_actionPerformed()
607 void addActionListener(ActionListener graduatedColorEditor)
609 if (colourEditor != null)
612 "IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
614 colourEditor = graduatedColorEditor;
618 * Answers the last colour setting selected by user - either oldcs (which may
619 * be a java.awt.Color) or the new GraduatedColor
623 FeatureColourI getLastColour()