more graduated feature support
[jalview.git] / src / jalview / gui / FeatureColourChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.util.*;
22
23 import java.awt.*;
24 import java.awt.event.*;
25 import javax.swing.*;
26 import javax.swing.event.*;
27
28 import jalview.datamodel.*;
29 import jalview.schemes.*;
30 import java.awt.Dimension;
31
32 public class FeatureColourChooser extends JPanel
33 {
34   JInternalFrame frame;
35   
36   FeatureRenderer fr;
37
38   FeatureSettings fs;
39
40   GraduatedColor cs;
41   Object oldcs;
42
43   Hashtable oldgroupColours;
44   
45   AlignmentPanel ap;
46   
47
48   boolean adjusting = false;
49
50   private float min;
51
52   private float max;
53   String type = null;
54   public FeatureColourChooser(FeatureSettings fsettings, String type)
55   {
56     this.fs = fsettings;
57     this.type = type;
58     fr = fsettings.fr;
59     ap = fr.ap;
60     frame = new JInternalFrame();
61     frame.setContentPane(this);
62     frame.setLayer(JLayeredPane.PALETTE_LAYER);
63     Desktop.addInternalFrame(frame, "Graduated Feature Colour for "+type, 480, 145);
64
65     slider.addChangeListener(new ChangeListener()
66     {
67       public void stateChanged(ChangeEvent evt)
68       {
69         if (!adjusting)
70         {
71           thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
72           valueChanged();
73         }
74       }
75     });
76     slider.addMouseListener(new MouseAdapter()
77     {
78       public void mouseReleased(MouseEvent evt)
79       {
80         if (fr.ap!=null) { fr.ap.paintAlignment(true); };
81       }
82     });
83
84     float mm[] = ((float[][]) fr.minmax.get(type))[0];
85     min = mm[0];
86     max = mm[1];
87     oldcs = fr.featureColours.get(type);
88     if (oldcs instanceof GraduatedColor)
89     {
90       if (((GraduatedColor)oldcs).isAutoScale())
91       {
92         // update the scale
93         cs = new GraduatedColor((GraduatedColor) oldcs, min, max);
94       } else {
95         cs = new GraduatedColor((GraduatedColor) oldcs);
96       }
97     } else {
98       // promote original color to a graduated color
99       Color bl = Color.black;
100       if (oldcs instanceof Color)
101       {
102         bl = (Color) oldcs;
103       }
104       // original colour becomes the maximum colour
105       cs = new GraduatedColor(Color.white,bl,mm[0],mm[1]);
106     }
107     minColour.setBackground(cs.getMinColor());
108     maxColour.setBackground(cs.getMaxColor());
109     adjusting = true;
110     
111     try
112     {
113       jbInit();
114     } catch (Exception ex)
115     {
116     }
117     // update the gui from threshold state
118     thresholdIsMin.setSelected(!cs.isAutoScale());
119     if (cs.getThreshType()!=AnnotationColourGradient.NO_THRESHOLD)
120     {
121       // initialise threshold slider and selector
122       threshold.setSelectedIndex(cs.getThreshType()==AnnotationColourGradient.ABOVE_THRESHOLD ? 1 : 2);
123             slider.setEnabled(true);
124       thresholdValue.setEnabled(true);
125       threshline = new jalview.datamodel.GraphLine(
126                         (max - min) / 2f,
127                         "Threshold", Color.black);
128       
129     }
130
131     adjusting = false;
132
133     changeColour();
134
135   }
136
137   public FeatureColourChooser()
138   {
139     try
140     {
141       jbInit();
142     } catch (Exception ex)
143     {
144       ex.printStackTrace();
145     }
146   }
147
148   private void jbInit() throws Exception
149   {
150     minColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
151     minColour.setBorder(BorderFactory.createEtchedBorder());
152     minColour.setPreferredSize(new Dimension(40, 20));
153     minColour.setToolTipText("Minimum Colour");
154     minColour.addMouseListener(new MouseAdapter()
155     {
156       public void mousePressed(MouseEvent e)
157       {
158         if (minColour.isEnabled())
159         {
160           minColour_actionPerformed();
161         }
162       }
163     });
164     maxColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
165     maxColour.setBorder(BorderFactory.createEtchedBorder());
166     maxColour.setPreferredSize(new Dimension(40, 20));
167     maxColour.setToolTipText("Maximum Colour");
168     maxColour.addMouseListener(new MouseAdapter()
169     {
170       public void mousePressed(MouseEvent e)
171       {
172         if (maxColour.isEnabled())
173         {
174           maxColour_actionPerformed();
175         }
176       }
177     });
178     ok.setOpaque(false);
179     ok.setText("OK");
180     ok.addActionListener(new ActionListener()
181     {
182       public void actionPerformed(ActionEvent e)
183       {
184         ok_actionPerformed(e);
185       }
186     });
187     cancel.setOpaque(false);
188     cancel.setText("Cancel");
189     cancel.addActionListener(new ActionListener()
190     {
191       public void actionPerformed(ActionEvent e)
192       {
193         cancel_actionPerformed(e);
194       }
195     });
196     this.setLayout(borderLayout1);
197     jPanel2.setLayout(flowLayout1);
198     jPanel1.setBackground(Color.white);
199     jPanel2.setBackground(Color.white);
200     threshold.addActionListener(new ActionListener()
201     {
202       public void actionPerformed(ActionEvent e)
203       {
204         threshold_actionPerformed(e);
205       }
206     });
207     threshold.addItem("No Threshold"); // index 0
208     threshold.addItem("Above Threshold"); // index 1
209     threshold.addItem("Below Threshold"); // index 2
210     jPanel3.setLayout(flowLayout2);
211     thresholdValue.addActionListener(new ActionListener()
212     {
213       public void actionPerformed(ActionEvent e)
214       {
215         thresholdValue_actionPerformed(e);
216       }
217     });
218     slider.setPaintLabels(false);
219     slider.setPaintTicks(true);
220     slider.setBackground(Color.white);
221     slider.setEnabled(false);
222     slider.setOpaque(false);
223     slider.setPreferredSize(new Dimension(100, 32));
224     thresholdValue.setEnabled(false);
225     thresholdValue.setColumns(7);
226     jPanel3.setBackground(Color.white);
227     currentColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
228     currentColours.setOpaque(false);
229     currentColours.setText("Use Original Colours");
230     currentColours.addActionListener(new ActionListener()
231     {
232       public void actionPerformed(ActionEvent e)
233       {
234         currentColours_actionPerformed(e);
235       }
236     });
237     thresholdIsMin.setBackground(Color.white);
238     thresholdIsMin.setText("Threshold is Min/Max");
239     thresholdIsMin.addActionListener(new ActionListener()
240     {
241       public void actionPerformed(ActionEvent actionEvent)
242       {
243         thresholdIsMin_actionPerformed(actionEvent);
244       }
245     });
246     jPanel1.add(ok);
247     jPanel1.add(cancel);
248     // jPanel2.add(currentColours);
249     jPanel2.add(minColour);
250     jPanel2.add(maxColour);
251     this.add(jPanel3, java.awt.BorderLayout.CENTER);
252     jPanel3.add(threshold);
253     jPanel3.add(slider);
254     jPanel3.add(thresholdValue);
255     jPanel3.add(thresholdIsMin);
256     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
257     this.add(jPanel2, java.awt.BorderLayout.NORTH);
258   }
259
260
261   JPanel minColour = new JPanel();
262
263   JPanel maxColour = new JPanel();
264
265   JButton ok = new JButton();
266
267   JButton cancel = new JButton();
268
269   JPanel jPanel1 = new JPanel();
270
271   JPanel jPanel2 = new JPanel();
272
273   BorderLayout borderLayout1 = new BorderLayout();
274
275   JComboBox threshold = new JComboBox();
276
277   FlowLayout flowLayout1 = new FlowLayout();
278
279   JPanel jPanel3 = new JPanel();
280
281   FlowLayout flowLayout2 = new FlowLayout();
282
283   JSlider slider = new JSlider();
284
285   JTextField thresholdValue = new JTextField(20);
286   // TODO refactor to tolower flag
287   JCheckBox currentColours = new JCheckBox();
288
289   JCheckBox thresholdIsMin = new JCheckBox();
290
291   private GraphLine threshline;
292
293   public void minColour_actionPerformed()
294   {
295     Color col = JColorChooser.showDialog(this,
296             "Select Colour for Minimum Value", minColour.getBackground());
297     if (col != null)
298     {
299       minColour.setBackground(col);
300     }
301     minColour.repaint();
302     changeColour();
303   }
304
305   public void maxColour_actionPerformed()
306   {
307     Color col = JColorChooser.showDialog(this,
308             "Select Colour for Maximum Value", maxColour.getBackground());
309     if (col != null)
310     {
311       maxColour.setBackground(col);
312     }
313     maxColour.repaint();
314     changeColour();
315   }
316
317   void changeColour()
318   {
319     // Check if combobox is still adjusting
320     if (adjusting)
321     {
322       return;
323     }
324
325
326     int aboveThreshold = AnnotationColourGradient.NO_THRESHOLD;
327     if (threshold.getSelectedItem().equals("Above Threshold"))
328     {
329       aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
330     }
331     else if (threshold.getSelectedItem().equals("Below Threshold"))
332     {
333       aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
334     } 
335
336     slider.setEnabled(true);
337     thresholdValue.setEnabled(true);
338     GraduatedColor acg = new GraduatedColor(minColour.getBackground(), maxColour.getBackground(), min, max);
339
340     if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
341     {
342       slider.setEnabled(false);
343       thresholdValue.setEnabled(false);
344       thresholdValue.setText("");
345     }
346     else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
347             && threshline == null)
348     {
349       // todo visual indication of feature threshold
350       threshline = new jalview.datamodel.GraphLine(
351                       (max - min) / 2f,
352                       "Threshold", Color.black);
353     }
354
355     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
356     {
357       adjusting = true;
358       acg.setThresh(threshline.value);
359
360       float range = max * 1000f
361               - min * 1000f;
362
363       slider.setMinimum((int) (min * 1000));
364       slider.setMaximum((int) (max * 1000));
365       slider.setValue((int) (threshline.value * 1000));
366       thresholdValue.setText(threshline.value + "");
367       slider.setMajorTickSpacing((int) (range / 10f));
368       slider.setEnabled(true);
369       thresholdValue.setEnabled(true);
370       adjusting = false;
371     }
372
373     acg.setThreshType(aboveThreshold);
374     if (thresholdIsMin.isSelected() && aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
375     {
376       acg.setAutoScaled(false);
377       if (aboveThreshold==AnnotationColourGradient.ABOVE_THRESHOLD)
378       { 
379         acg = new GraduatedColor(acg, threshline.value, max);
380       } else { 
381         acg = new GraduatedColor(acg, min,threshline.value);
382       }
383     } else {
384       acg.setAutoScaled(true);
385     }
386     
387     fr.featureColours.put(type,acg);
388     cs = acg;
389     ap.paintAlignment(false);
390   }
391
392   public void ok_actionPerformed(ActionEvent e)
393   {
394     changeColour();
395     try
396     {
397       frame.setClosed(true);
398     } catch (Exception ex)
399     {
400     }
401   }
402
403   public void cancel_actionPerformed(ActionEvent e)
404   {
405     reset();
406     try
407     {
408       frame.setClosed(true);
409     } catch (Exception ex)
410     {
411     }
412   }
413
414   void reset()
415   {
416     fr.featureColours.put(type, oldcs);
417   }
418
419   public void thresholdCheck_actionPerformed(ActionEvent e)
420   {
421     changeColour();
422   }
423
424   public void annotations_actionPerformed(ActionEvent e)
425   {
426     changeColour();
427   }
428
429   public void threshold_actionPerformed(ActionEvent e)
430   {
431     changeColour();
432   }
433
434   public void thresholdValue_actionPerformed(ActionEvent e)
435   {
436     try
437     {
438       float f = Float.parseFloat(thresholdValue.getText());
439       slider.setValue((int) (f * 1000));
440       threshline.value = f;
441     } catch (NumberFormatException ex)
442     {
443     }
444   }
445
446   public void valueChanged()
447   {
448     threshline.value = (float) slider.getValue() / 1000f;
449     cs.setThresh(threshline.value);
450     changeColour();
451     ap.paintAlignment(false);
452   }
453
454   public void currentColours_actionPerformed(ActionEvent e)
455   {
456     if (currentColours.isSelected())
457     {
458       reset();
459     }
460
461     maxColour.setEnabled(!currentColours.isSelected());
462     minColour.setEnabled(!currentColours.isSelected());
463
464     changeColour();
465   }
466
467   public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
468   {
469     changeColour();
470   }
471
472 }