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.MouseAdapter;
35 import java.awt.event.MouseEvent;
37 import javax.swing.BorderFactory;
38 import javax.swing.JCheckBox;
39 import javax.swing.JColorChooser;
40 import javax.swing.JComboBox;
41 import javax.swing.JLabel;
42 import javax.swing.JPanel;
43 import javax.swing.JSlider;
44 import javax.swing.JTextField;
45 import javax.swing.border.LineBorder;
46 import javax.swing.event.ChangeEvent;
47 import javax.swing.event.ChangeListener;
49 public class FeatureColourChooser extends JalviewDialog
51 // FeatureSettings fs;
52 private FeatureRenderer fr;
54 private FeatureColourI cs;
56 private FeatureColourI oldcs;
58 private AlignmentPanel ap;
60 private boolean adjusting = false;
62 final private float min;
64 final private float max;
66 final private float scaleFactor;
68 private String type = null;
70 private JPanel minColour = new JPanel();
72 private JPanel maxColour = new JPanel();
74 private JComboBox<String> threshold = new JComboBox<>();
76 private JSlider slider = new JSlider();
78 private JTextField thresholdValue = new JTextField(20);
80 // TODO implement GUI for tolower flag
81 // JCheckBox toLower = new JCheckBox();
83 private JCheckBox thresholdIsMin = new JCheckBox();
85 private JCheckBox colourByLabel = new JCheckBox();
87 private GraphLine threshline;
89 private Color oldmaxColour;
91 private Color oldminColour;
93 private ActionListener colourEditor = null;
101 public FeatureColourChooser(FeatureRenderer frender, String theType)
103 this(frender, false, theType);
107 * Constructor, with option to make a blocking dialog (has to complete in the
108 * AWT event queue thread). Currently this option is always set to false.
114 FeatureColourChooser(FeatureRenderer frender, boolean blocking,
120 String title = MessageManager.formatMessage(
121 "label.graduated_color_for_params", new String[] { theType });
122 initDialogFrame(this, true, blocking, title, 480, 185);
124 slider.addChangeListener(new ChangeListener()
127 public void stateChanged(ChangeEvent evt)
131 thresholdValue.setText((slider.getValue() / scaleFactor) + "");
132 sliderValueChanged();
136 slider.addMouseListener(new MouseAdapter()
139 public void mouseReleased(MouseEvent evt)
142 * only update Overview and/or structure colouring
143 * when threshold slider drag ends (mouse up)
147 ap.paintAlignment(true);
152 float mm[] = fr.getMinMax().get(theType)[0];
157 * ensure scale factor allows a scaled range with
158 * 10 integer divisions ('ticks'); if we have got here,
159 * we should expect that max != min
161 scaleFactor = (max == min) ? 1f : 100f / (max - min);
163 oldcs = fr.getFeatureColours().get(theType);
164 if (!oldcs.isSimpleColour())
166 if (oldcs.isAutoScaled())
169 cs = new FeatureColour((FeatureColour) oldcs, min, max);
173 cs = new FeatureColour((FeatureColour) oldcs);
178 // promote original color to a graduated color
179 Color bl = oldcs.getColour();
184 // original colour becomes the maximum colour
185 cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
186 cs.setColourByLabel(false);
188 minColour.setBackground(oldminColour = cs.getMinColour());
189 maxColour.setBackground(oldmaxColour = cs.getMaxColour());
195 } catch (Exception ex)
198 // update the gui from threshold state
199 thresholdIsMin.setSelected(!cs.isAutoScaled());
200 colourByLabel.setSelected(cs.isColourByLabel());
201 if (cs.hasThreshold())
203 // initialise threshold slider and selector
204 threshold.setSelectedIndex(cs.isAboveThreshold() ? 1 : 2);
205 slider.setEnabled(true);
206 slider.setValue((int) (cs.getThreshold() * scaleFactor));
207 thresholdValue.setEnabled(true);
208 threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black);
209 threshline.value = cs.getThreshold();
218 private void jbInit() throws Exception
221 minColour.setFont(JvSwingUtils.getLabelFont());
222 minColour.setBorder(BorderFactory.createLineBorder(Color.black));
223 minColour.setPreferredSize(new Dimension(40, 20));
224 minColour.setToolTipText(MessageManager.getString("label.min_colour"));
225 minColour.addMouseListener(new MouseAdapter()
228 public void mousePressed(MouseEvent e)
230 if (minColour.isEnabled())
232 minColour_actionPerformed();
236 maxColour.setFont(JvSwingUtils.getLabelFont());
237 maxColour.setBorder(BorderFactory.createLineBorder(Color.black));
238 maxColour.setPreferredSize(new Dimension(40, 20));
239 maxColour.setToolTipText(MessageManager.getString("label.max_colour"));
240 maxColour.addMouseListener(new MouseAdapter()
243 public void mousePressed(MouseEvent e)
245 if (maxColour.isEnabled())
247 maxColour_actionPerformed();
251 maxColour.setBorder(new LineBorder(Color.black));
252 JLabel minText = new JLabel(MessageManager.getString("label.min"));
253 minText.setFont(JvSwingUtils.getLabelFont());
254 JLabel maxText = new JLabel(MessageManager.getString("label.max"));
255 maxText.setFont(JvSwingUtils.getLabelFont());
256 this.setLayout(new BorderLayout());
257 JPanel jPanel1 = new JPanel();
258 jPanel1.setBackground(Color.white);
259 JPanel jPanel2 = new JPanel();
260 jPanel2.setLayout(new FlowLayout());
261 jPanel2.setBackground(Color.white);
262 threshold.addActionListener(new ActionListener()
265 public void actionPerformed(ActionEvent e)
267 threshold_actionPerformed();
270 threshold.setToolTipText(MessageManager
271 .getString("label.threshold_feature_display_by_score"));
272 threshold.addItem(MessageManager
273 .getString("label.threshold_feature_no_threshold")); // index 0
274 threshold.addItem(MessageManager
275 .getString("label.threshold_feature_above_threshold")); // index 1
276 threshold.addItem(MessageManager
277 .getString("label.threshold_feature_below_threshold")); // index 2
279 JPanel jPanel3 = new JPanel();
280 jPanel3.setLayout(new FlowLayout());
281 thresholdValue.addActionListener(new ActionListener()
284 public void actionPerformed(ActionEvent e)
286 thresholdValue_actionPerformed();
289 slider.setPaintLabels(false);
290 slider.setPaintTicks(true);
291 slider.setBackground(Color.white);
292 slider.setEnabled(false);
293 slider.setOpaque(false);
294 slider.setPreferredSize(new Dimension(100, 32));
295 slider.setToolTipText(MessageManager
296 .getString("label.adjust_threshold"));
297 thresholdValue.setEnabled(false);
298 thresholdValue.setColumns(7);
299 jPanel3.setBackground(Color.white);
300 thresholdIsMin.setBackground(Color.white);
301 thresholdIsMin.setText(MessageManager
302 .getString("label.threshold_minmax"));
303 thresholdIsMin.setToolTipText(MessageManager
304 .getString("label.toggle_absolute_relative_display_threshold"));
305 thresholdIsMin.addActionListener(new ActionListener()
308 public void actionPerformed(ActionEvent actionEvent)
310 thresholdIsMin_actionPerformed();
313 colourByLabel.setBackground(Color.white);
315 .setText(MessageManager.getString("label.colour_by_label"));
317 .setToolTipText(MessageManager
318 .getString("label.display_features_same_type_different_label_using_different_colour"));
319 colourByLabel.addActionListener(new ActionListener()
322 public void actionPerformed(ActionEvent actionEvent)
324 colourByLabel_actionPerformed();
328 JPanel colourPanel = new JPanel();
329 colourPanel.setBackground(Color.white);
332 jPanel2.add(colourByLabel, BorderLayout.WEST);
333 jPanel2.add(colourPanel, BorderLayout.EAST);
334 colourPanel.add(minText);
335 colourPanel.add(minColour);
336 colourPanel.add(maxText);
337 colourPanel.add(maxColour);
338 this.add(jPanel3, BorderLayout.CENTER);
339 jPanel3.add(threshold);
341 jPanel3.add(thresholdValue);
342 jPanel3.add(thresholdIsMin);
343 this.add(jPanel1, BorderLayout.SOUTH);
344 this.add(jPanel2, BorderLayout.NORTH);
348 * Action on clicking the 'minimum colour' - open a colour chooser dialog, and
349 * set the selected colour (if the user does not cancel out of the dialog)
351 protected void minColour_actionPerformed()
353 Color col = JColorChooser.showDialog(this,
354 MessageManager.getString("label.select_colour_minimum_value"),
355 minColour.getBackground());
358 minColour.setBackground(col);
359 minColour.setForeground(col);
366 * Action on clicking the 'maximum colour' - open a colour chooser dialog, and
367 * set the selected colour (if the user does not cancel out of the dialog)
369 protected void maxColour_actionPerformed()
371 Color col = JColorChooser.showDialog(this,
372 MessageManager.getString("label.select_colour_maximum_value"),
373 maxColour.getBackground());
376 maxColour.setBackground(col);
377 maxColour.setForeground(col);
384 * Constructs and sets the selected colour options as the colour for the
385 * feature type, and repaints the alignment, and optionally the Overview
386 * and/or structure viewer if open
388 * @param updateOverview
390 void changeColour(boolean updateOverview)
392 // Check if combobox is still adjusting
398 boolean aboveThreshold = false;
399 boolean belowThreshold = false;
400 if (threshold.getSelectedIndex() == 1)
402 aboveThreshold = true;
404 else if (threshold.getSelectedIndex() == 2)
406 belowThreshold = true;
408 boolean hasThreshold = aboveThreshold || belowThreshold;
410 slider.setEnabled(true);
411 thresholdValue.setEnabled(true);
414 if (cs.isColourByLabel())
416 acg = new FeatureColour(oldminColour, oldmaxColour, min, max);
420 acg = new FeatureColour(oldminColour = minColour.getBackground(),
421 oldmaxColour = maxColour.getBackground(), min, max);
426 slider.setEnabled(false);
427 thresholdValue.setEnabled(false);
428 thresholdValue.setText("");
429 thresholdIsMin.setEnabled(false);
431 else if (threshline == null)
434 * todo not yet implemented: visual indication of feature threshold
436 threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black);
442 acg.setThreshold(threshline.value);
444 float range = (max - min) * scaleFactor;
446 slider.setMinimum((int) (min * scaleFactor));
447 slider.setMaximum((int) (max * scaleFactor));
448 // slider.setValue((int) (threshline.value * scaleFactor));
449 slider.setValue(Math.round(threshline.value * scaleFactor));
450 thresholdValue.setText(threshline.value + "");
451 slider.setMajorTickSpacing((int) (range / 10f));
452 slider.setEnabled(true);
453 thresholdValue.setEnabled(true);
454 thresholdIsMin.setEnabled(!colourByLabel.isSelected());
458 acg.setAboveThreshold(aboveThreshold);
459 acg.setBelowThreshold(belowThreshold);
460 if (thresholdIsMin.isSelected() && hasThreshold)
462 acg.setAutoScaled(false);
465 acg = new FeatureColour((FeatureColour) acg, threshline.value, max);
469 acg = new FeatureColour((FeatureColour) acg, min, threshline.value);
474 acg.setAutoScaled(true);
476 acg.setColourByLabel(colourByLabel.isSelected());
477 if (acg.isColourByLabel())
479 maxColour.setEnabled(false);
480 minColour.setEnabled(false);
481 maxColour.setBackground(this.getBackground());
482 maxColour.setForeground(this.getBackground());
483 minColour.setBackground(this.getBackground());
484 minColour.setForeground(this.getBackground());
489 maxColour.setEnabled(true);
490 minColour.setEnabled(true);
491 maxColour.setBackground(oldmaxColour);
492 minColour.setBackground(oldminColour);
493 maxColour.setForeground(oldmaxColour);
494 minColour.setForeground(oldminColour);
496 fr.setColour(type, acg);
498 ap.paintAlignment(updateOverview);
502 protected void raiseClosed()
504 if (this.colourEditor != null)
506 colourEditor.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
511 public void okPressed()
517 public void cancelPressed()
523 * Action when the user cancels the dialog. All previous settings should be
524 * restored and rendered on the alignment, and any linked Overview window or
529 fr.setColour(type, oldcs);
530 ap.paintAlignment(true);
535 * Action on change of choice of No / Above / Below Threshold
537 protected void threshold_actionPerformed()
543 * Action on text entry of a threshold value
545 protected void thresholdValue_actionPerformed()
549 float f = Float.parseFloat(thresholdValue.getText());
550 slider.setValue((int) (f * scaleFactor));
551 threshline.value = f;
554 * force repaint of any Overview window or structure
556 ap.paintAlignment(true);
557 } catch (NumberFormatException ex)
563 * Action on change of threshold slider value. This may be done interactively
564 * (by moving the slider), or programmatically (to update the slider after
565 * manual input of a threshold value).
567 protected void sliderValueChanged()
570 * squash rounding errors by forcing min/max of slider to
571 * actual min/max of feature score range
573 int value = slider.getValue();
574 threshline.value = value == slider.getMaximum() ? max
575 : (value == slider.getMinimum() ? min : value / scaleFactor);
576 cs.setThreshold(threshline.value);
579 * repaint alignment, but not Overview or structure,
580 * to avoid overload while dragging the slider
585 protected void thresholdIsMin_actionPerformed()
590 protected void colourByLabel_actionPerformed()
595 void addActionListener(ActionListener graduatedColorEditor)
597 if (colourEditor != null)
600 .println("IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
602 colourEditor = graduatedColorEditor;
606 * Answers the last colour setting selected by user - either oldcs (which may
607 * be a java.awt.Color) or the new GraduatedColor
611 FeatureColourI getLastColour()