JAL-3438 spotless for 2.11.2.0
[jalview.git] / src / jalview / appletgui / FeatureColourChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
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.
11  *  
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.
16  * 
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.
20  */
21 package jalview.appletgui;
22
23 import jalview.api.FeatureColourI;
24 import jalview.datamodel.GraphLine;
25 import jalview.schemes.AnnotationColourGradient;
26 import jalview.schemes.FeatureColour;
27 import jalview.util.MessageManager;
28
29 import java.awt.Checkbox;
30 import java.awt.Choice;
31 import java.awt.Color;
32 import java.awt.Dimension;
33 import java.awt.FlowLayout;
34 import java.awt.Font;
35 import java.awt.Frame;
36 import java.awt.GridLayout;
37 import java.awt.Label;
38 import java.awt.Panel;
39 import java.awt.Scrollbar;
40 import java.awt.TextField;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.awt.event.AdjustmentEvent;
44 import java.awt.event.AdjustmentListener;
45 import java.awt.event.FocusAdapter;
46 import java.awt.event.FocusEvent;
47 import java.awt.event.ItemEvent;
48 import java.awt.event.ItemListener;
49 import java.awt.event.MouseEvent;
50 import java.awt.event.MouseListener;
51
52 public class FeatureColourChooser extends Panel implements ActionListener,
53         AdjustmentListener, ItemListener, MouseListener
54 {
55   /*
56    * the absolute min-max range of a feature score is scaled to 
57    * 1000 positions on the colour threshold slider
58    */
59   private static final int SCALE_FACTOR_1K = 1000;
60
61   private static final String COLON = ":";
62
63   private JVDialog frame;
64
65   private Frame owner;
66
67   private FeatureRenderer fr;
68
69   private FeatureSettings fs = null;
70
71   private FeatureColourI cs;
72
73   private FeatureColourI oldcs;
74
75   private boolean adjusting = false;
76
77   private float min, max;
78
79   private String type = null;
80
81   private AlignFrame af = null;
82
83   private Panel minColour = new Panel();
84
85   private Panel maxColour = new Panel();
86
87   private Choice threshold = new Choice();
88
89   private Scrollbar slider = new Scrollbar(Scrollbar.HORIZONTAL);
90
91   private TextField thresholdValue = new TextField(20);
92
93   private Checkbox thresholdIsMin = new Checkbox();
94
95   private Checkbox colourFromLabel = new Checkbox();
96
97   private GraphLine threshline;
98
99   /**
100    * Constructor given a context AlignFrame and a feature type. This is used
101    * when opening the graduated colour dialog from the Amend Feature dialog.
102    * 
103    * @param alignFrame
104    * @param featureType
105    */
106   public FeatureColourChooser(AlignFrame alignFrame, String featureType)
107   {
108     this.af = alignFrame;
109     init(alignFrame.getSeqcanvas().getFeatureRenderer(), featureType);
110   }
111
112   /**
113    * Constructor given a context FeatureSettings and a feature type. This is
114    * used when opening the graduated colour dialog from Feature Settings.
115    * 
116    * @param fsettings
117    * @param featureType
118    */
119   public FeatureColourChooser(FeatureSettings fsettings, String featureType)
120   {
121     this.fs = fsettings;
122     init(fsettings.fr, featureType);
123   }
124
125   private void init(FeatureRenderer frenderer, String featureType)
126   {
127     this.type = featureType;
128     fr = frenderer;
129     float mm[] = fr.getMinMax().get(type)[0];
130     min = mm[0];
131     max = mm[1];
132     threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black);
133     oldcs = fr.getFeatureColours().get(type);
134     if (oldcs.isGraduatedColour())
135     {
136       threshline.value = oldcs.getThreshold();
137       cs = new FeatureColour(oldcs.getColour(), oldcs.getMinColour(),
138               oldcs.getMaxColour(), oldcs.getNoColour(), min, max);
139     }
140     else
141     {
142       // promote original color to a graduated color
143       Color bl = Color.black;
144       if (oldcs.isSimpleColour())
145       {
146         bl = oldcs.getColour();
147       }
148       // original colour becomes the maximum colour
149       cs = new FeatureColour(bl, Color.white, bl, Color.white, mm[0],
150               mm[1]);
151     }
152     minColour.setBackground(cs.getMinColour());
153     maxColour.setBackground(cs.getMaxColour());
154     minColour.setForeground(cs.getMinColour());
155     maxColour.setForeground(cs.getMaxColour());
156     colourFromLabel.setState(cs.isColourByLabel());
157     adjusting = true;
158
159     try
160     {
161       jbInit();
162     } catch (Exception ex)
163     {
164     }
165     threshold.select(
166             cs.isAboveThreshold() ? 1 : (cs.isBelowThreshold() ? 2 : 0));
167
168     adjusting = false;
169     changeColour(true);
170     colourFromLabel.addItemListener(this);
171     slider.addAdjustmentListener(this);
172     slider.addMouseListener(this);
173     owner = (af != null) ? af : fs.frame;
174     frame = new JVDialog(owner, MessageManager
175             .formatMessage("label.variable_color_for", new String[]
176             { type }), true, 480, 248);
177     frame.setMainPanel(this);
178     validate();
179     frame.setVisible(true);
180     if (frame.accept)
181     {
182       changeColour(true);
183     }
184     else
185     {
186       // cancel
187       reset();
188       frame.setVisible(false);
189     }
190   }
191
192   public FeatureColourChooser()
193   {
194     try
195     {
196       jbInit();
197     } catch (Exception ex)
198     {
199       ex.printStackTrace();
200     }
201   }
202
203   private void jbInit() throws Exception
204   {
205     Label minLabel = new Label(
206             MessageManager.getString("label.min_value") + COLON);
207     Label maxLabel = new Label(
208             MessageManager.getString("label.max_value") + COLON);
209     minLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
210     maxLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
211     // minColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
212     // minColour.setLabel("Min Colour");
213
214     minColour.setBounds(0, 0, 40, 27);
215     maxColour.setBounds(0, 0, 40, 27);
216     minColour.addMouseListener(this);
217
218     maxColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
219     maxColour.addMouseListener(this);
220
221     thresholdIsMin.addItemListener(this);
222
223     this.setLayout(new GridLayout(4, 1));
224     Panel jPanel1 = new Panel();
225     jPanel1.setLayout(new FlowLayout());
226     Panel jPanel2 = new Panel();
227     jPanel2.setLayout(new FlowLayout());
228     Panel jPanel3 = new Panel();
229     jPanel3.setLayout(new GridLayout(1, 1));
230     Panel jPanel4 = new Panel();
231     jPanel4.setLayout(new FlowLayout());
232     jPanel1.setBackground(Color.white);
233     jPanel2.setBackground(Color.white);
234     jPanel4.setBackground(Color.white);
235     threshold.addItemListener(this);
236     threshold.addItem(MessageManager
237             .getString("label.threshold_feature_no_threshold"));
238     threshold.addItem(MessageManager
239             .getString("label.threshold_feature_above_threshold"));
240     threshold.addItem(MessageManager
241             .getString("label.threshold_feature_below_threshold"));
242     thresholdValue.addActionListener(this);
243     thresholdValue.addFocusListener(new FocusAdapter()
244     {
245       @Override
246       public void focusLost(FocusEvent e)
247       {
248         thresholdValue_actionPerformed();
249       }
250     });
251     slider.setBackground(Color.white);
252     slider.setEnabled(false);
253     slider.setSize(new Dimension(93, 21));
254     thresholdValue.setEnabled(false);
255     thresholdValue.setSize(new Dimension(79, 22)); // setBounds(new
256                                                    // Rectangle(248, 2, 79,
257                                                    // 22));
258     thresholdValue.setColumns(5);
259     jPanel3.setBackground(Color.white);
260
261     colourFromLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
262     colourFromLabel
263             .setLabel(MessageManager.getString("label.colour_by_label"));
264     colourFromLabel.setSize(new Dimension(139, 22));
265     // threshold.setBounds(new Rectangle(11, 3, 139, 22));
266     thresholdIsMin.setBackground(Color.white);
267     thresholdIsMin
268             .setLabel(MessageManager.getString("label.threshold_minmax"));
269     thresholdIsMin.setSize(new Dimension(135, 23));
270     // thresholdIsMin.setBounds(new Rectangle(328, 3, 135, 23));
271     jPanel1.add(minLabel);
272     jPanel1.add(minColour);
273     jPanel1.add(maxLabel);
274     jPanel1.add(maxColour);
275     jPanel1.add(colourFromLabel);
276     jPanel2.add(threshold);
277     jPanel3.add(slider);
278     jPanel4.add(thresholdValue);
279     jPanel4.add(thresholdIsMin);
280     this.add(jPanel1);// , java.awt.BorderLayout.NORTH);
281     this.add(jPanel2);// , java.awt.BorderLayout.NORTH);
282     this.add(jPanel3);// , java.awt.BorderLayout.CENTER);
283     this.add(jPanel4);// , java.awt.BorderLayout.CENTER);
284   }
285
286   @Override
287   public void actionPerformed(ActionEvent evt)
288   {
289     if (evt.getSource() == thresholdValue)
290     {
291       thresholdValue_actionPerformed();
292     }
293     else if (evt.getSource() == minColour)
294     {
295       minColour_actionPerformed(null);
296     }
297     else if (evt.getSource() == maxColour)
298     {
299       maxColour_actionPerformed(null);
300     }
301     else
302     {
303       changeColour(true);
304     }
305   }
306
307   /**
308    * Action on input of a value for colour score threshold
309    */
310   protected void thresholdValue_actionPerformed()
311   {
312     try
313     {
314       float f = Float.valueOf(thresholdValue.getText()).floatValue();
315       slider.setValue((int) (f * SCALE_FACTOR_1K));
316       adjustmentValueChanged(null);
317
318       /*
319        * force repaint of any Overview window or structure
320        */
321       changeColour(true);
322     } catch (NumberFormatException ex)
323     {
324     }
325   }
326
327   @Override
328   public void itemStateChanged(ItemEvent evt)
329   {
330     maxColour.setEnabled(!colourFromLabel.getState());
331     minColour.setEnabled(!colourFromLabel.getState());
332     changeColour(true);
333   }
334
335   /**
336    * Handler called when the value of the threshold slider changes, either by
337    * user action or programmatically
338    */
339   @Override
340   public void adjustmentValueChanged(AdjustmentEvent evt)
341   {
342     if (!adjusting)
343     {
344       thresholdValue.setText((slider.getValue() / 1000f) + "");
345       valueChanged();
346     }
347   }
348
349   /**
350    * Responds to a change of colour threshold by computing the absolute value
351    * and refreshing the alignment.
352    */
353   protected void valueChanged()
354   {
355     threshline.value = slider.getValue() / 1000f;
356     cs.setThreshold(threshline.value);
357     changeColour(false);
358     PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
359   }
360
361   public void minColour_actionPerformed(Color newCol)
362   {
363     if (newCol == null)
364     {
365       new UserDefinedColours(this, minColour.getBackground(), owner,
366               MessageManager
367                       .getString("label.select_colour_minimum_value"));
368     }
369     else
370     {
371       minColour.setBackground(newCol);
372       minColour.setForeground(newCol);
373       minColour.repaint();
374       changeColour(true);
375     }
376
377   }
378
379   public void maxColour_actionPerformed(Color newCol)
380   {
381     if (newCol == null)
382     {
383       new UserDefinedColours(this, maxColour.getBackground(), owner,
384               MessageManager
385                       .getString("label.select_colour_maximum_value"));
386     }
387     else
388     {
389       maxColour.setBackground(newCol);
390       maxColour.setForeground(newCol);
391       maxColour.repaint();
392       changeColour(true);
393     }
394   }
395
396   void changeColour(boolean updateOverview)
397   {
398     // Check if combobox is still adjusting
399     if (adjusting)
400     {
401       return;
402     }
403
404     int thresholdOption = AnnotationColourGradient.NO_THRESHOLD;
405     if (threshold.getSelectedIndex() == 1)
406     {
407       thresholdOption = AnnotationColourGradient.ABOVE_THRESHOLD;
408     }
409     else if (threshold.getSelectedIndex() == 2)
410     {
411       thresholdOption = AnnotationColourGradient.BELOW_THRESHOLD;
412     }
413
414     slider.setEnabled(true);
415     thresholdValue.setEnabled(true);
416     Color minc = minColour.getBackground();
417     Color maxc = maxColour.getBackground();
418     FeatureColour acg = new FeatureColour(maxc, minc, maxc, minc, min, max);
419
420     acg.setColourByLabel(colourFromLabel.getState());
421     maxColour.setEnabled(!colourFromLabel.getState());
422     minColour.setEnabled(!colourFromLabel.getState());
423     if (thresholdOption == AnnotationColourGradient.NO_THRESHOLD)
424     {
425       slider.setEnabled(false);
426       thresholdValue.setEnabled(false);
427       thresholdValue.setText("");
428     }
429
430     if (thresholdOption != AnnotationColourGradient.NO_THRESHOLD)
431     {
432       adjusting = true;
433       acg.setThreshold(threshline.value);
434
435       slider.setMinimum((int) (min * SCALE_FACTOR_1K));
436       slider.setMaximum((int) (max * SCALE_FACTOR_1K));
437       slider.setValue((int) (threshline.value * SCALE_FACTOR_1K));
438       thresholdValue.setText(threshline.value + "");
439       slider.setEnabled(true);
440       thresholdValue.setEnabled(true);
441       adjusting = false;
442     }
443
444     acg.setAboveThreshold(
445             thresholdOption == AnnotationColourGradient.ABOVE_THRESHOLD);
446     acg.setBelowThreshold(
447             thresholdOption == AnnotationColourGradient.BELOW_THRESHOLD);
448
449     if (thresholdIsMin.getState()
450             && thresholdOption != AnnotationColourGradient.NO_THRESHOLD)
451     {
452       if (thresholdOption == AnnotationColourGradient.ABOVE_THRESHOLD)
453       {
454         acg = new FeatureColour(acg.getColour(), acg.getMinColour(),
455                 acg.getMaxColour(), acg.getNoColour(), threshline.value,
456                 max);
457       }
458       else
459       {
460         acg = new FeatureColour(acg.getColour(), acg.getMinColour(),
461                 acg.getMaxColour(), acg.getNoColour(), min,
462                 threshline.value);
463       }
464     }
465
466     fr.setColour(type, acg);
467     cs = acg;
468     fs.selectionChanged(updateOverview);
469   }
470
471   void reset()
472   {
473     fr.setColour(type, oldcs);
474     fs.selectionChanged(true);
475   }
476
477   @Override
478   public void mouseClicked(MouseEvent evt)
479   {
480   }
481
482   @Override
483   public void mousePressed(MouseEvent evt)
484   {
485   }
486
487   @Override
488   public void mouseReleased(MouseEvent evt)
489   {
490     if (evt.getSource() == minColour)
491     {
492       minColour_actionPerformed(null);
493     }
494     else if (evt.getSource() == maxColour)
495     {
496       maxColour_actionPerformed(null);
497     }
498     else
499     {
500       changeColour(true);
501       // PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
502     }
503   }
504
505   @Override
506   public void mouseEntered(MouseEvent evt)
507   {
508   }
509
510   @Override
511   public void mouseExited(MouseEvent evt)
512   {
513   }
514
515 }