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