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