gui/debug: integrate graduated feature colour editor into amend feature and feature...
[jalview.git] / src / jalview / gui / FeatureColourChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 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   JDialog frame;
35   
36
37 //  FeatureSettings fs;
38   FeatureRenderer fr;
39   
40   
41   private GraduatedColor cs;
42   private Object oldcs;
43   /**
44    * 
45    * @return the last colour setting selected by user - either oldcs (which may be a java.awt.Color) or the new GraduatedColor
46    */
47   public Object getLastColour() {
48     if (cs==null)
49     {
50       return oldcs;
51     }
52     return cs;
53   }
54   Hashtable oldgroupColours;
55   
56   AlignmentPanel ap;
57   
58
59   boolean adjusting = false;
60
61   private float min;
62
63   private float max;
64   String type = null;
65   public FeatureColourChooser(FeatureRenderer frender, String type)
66   {
67     this(frender,false,type);
68   }
69     public FeatureColourChooser(FeatureRenderer frender, boolean block, String type)
70     {
71     this.fr = frender;
72     this.type = type;
73     ap = fr.ap;
74     frame = new JDialog(Desktop.instance,true);
75     frame.setSize(480,185);
76     frame.setContentPane(this);
77     //frame.setLayer(JLayeredPane.PALETTE_LAYER);
78     //Desktop.addInternalFrame(frame, "Graduated Feature Colour for "+type, 480, 145);
79
80     slider.addChangeListener(new ChangeListener()
81     {
82       public void stateChanged(ChangeEvent evt)
83       {
84         if (!adjusting)
85         {
86           thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
87           valueChanged();
88         }
89       }
90     });
91     slider.addMouseListener(new MouseAdapter()
92     {
93       public void mouseReleased(MouseEvent evt)
94       {
95         if (ap!=null) { ap.paintAlignment(true); };
96       }
97     });
98
99     float mm[] = ((float[][]) fr.minmax.get(type))[0];
100     min = mm[0];
101     max = mm[1];
102     oldcs = fr.featureColours.get(type);
103     if (oldcs instanceof GraduatedColor)
104     {
105       if (((GraduatedColor)oldcs).isAutoScale())
106       {
107         // update the scale
108         cs = new GraduatedColor((GraduatedColor) oldcs, min, max);
109       } else {
110         cs = new GraduatedColor((GraduatedColor) oldcs);
111       }
112     } else {
113       // promote original color to a graduated color
114       Color bl = Color.black;
115       if (oldcs instanceof Color)
116       {
117         bl = (Color) oldcs;
118       }
119       // original colour becomes the maximum colour
120       cs = new GraduatedColor(Color.white,bl,mm[0],mm[1]);
121       cs.setColourByLabel(false);
122     }
123     minColour.setBackground(oldminColour=cs.getMinColor());
124     maxColour.setBackground(oldmaxColour=cs.getMaxColor());
125     adjusting = true;
126     
127     try
128     {
129       jbInit();
130     } catch (Exception ex)
131     {
132     }
133     // update the gui from threshold state
134     thresholdIsMin.setSelected(!cs.isAutoScale());
135     colourByLabel.setSelected(cs.isColourByLabel());
136     if (cs.getThreshType()!=AnnotationColourGradient.NO_THRESHOLD)
137     {
138       // initialise threshold slider and selector
139       threshold.setSelectedIndex(cs.getThreshType()==AnnotationColourGradient.ABOVE_THRESHOLD ? 1 : 2);
140             slider.setEnabled(true);
141       thresholdValue.setEnabled(true);
142       threshline = new jalview.datamodel.GraphLine(
143                         (max - min) / 2f,
144                         "Threshold", Color.black);
145       
146     }
147
148     adjusting = false;
149
150     changeColour();
151     if (!block)
152     {
153       new Thread(new Runnable() {
154
155       public void run()
156       {
157         frame.show();
158       }
159       
160     }).start();
161     } else {
162       frame.show();
163     }
164   }
165
166   public FeatureColourChooser()
167   {
168     try
169     {
170       jbInit();
171     } catch (Exception ex)
172     {
173       ex.printStackTrace();
174     }
175   }
176
177   private void jbInit() throws Exception
178   {
179     
180     minColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
181     minColour.setBorder(BorderFactory.createEtchedBorder());
182     minColour.setPreferredSize(new Dimension(40, 20));
183     minColour.setToolTipText("Minimum Colour");
184     minColour.addMouseListener(new MouseAdapter()
185     {
186       public void mousePressed(MouseEvent e)
187       {
188         if (minColour.isEnabled())
189         {
190           minColour_actionPerformed();
191         }
192       }
193     });
194     maxColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
195     maxColour.setBorder(BorderFactory.createEtchedBorder());
196     maxColour.setPreferredSize(new Dimension(40, 20));
197     maxColour.setToolTipText("Maximum Colour");
198     maxColour.addMouseListener(new MouseAdapter()
199     {
200       public void mousePressed(MouseEvent e)
201       {
202         if (maxColour.isEnabled())
203         {
204           maxColour_actionPerformed();
205         }
206       }
207     });
208     ok.setOpaque(false);
209     ok.setText("OK");
210     ok.addActionListener(new ActionListener()
211     {
212       public void actionPerformed(ActionEvent e)
213       {
214         ok_actionPerformed(e);
215       }
216     });
217     cancel.setOpaque(false);
218     cancel.setText("Cancel");
219     cancel.addActionListener(new ActionListener()
220     {
221       public void actionPerformed(ActionEvent e)
222       {
223         cancel_actionPerformed(e);
224       }
225     });
226     this.setLayout(borderLayout1);
227     jPanel2.setLayout(flowLayout1);
228     jPanel1.setBackground(Color.white);
229     jPanel2.setBackground(Color.white);
230     threshold.addActionListener(new ActionListener()
231     {
232       public void actionPerformed(ActionEvent e)
233       {
234         threshold_actionPerformed(e);
235       }
236     });
237     threshold.setToolTipText("Threshold the feature display by score.");
238     threshold.addItem("No Threshold"); // index 0
239     threshold.addItem("Above Threshold"); // index 1
240     threshold.addItem("Below Threshold"); // index 2
241     jPanel3.setLayout(flowLayout2);
242     thresholdValue.addActionListener(new ActionListener()
243     {
244       public void actionPerformed(ActionEvent e)
245       {
246         thresholdValue_actionPerformed(e);
247       }
248     });
249     slider.setPaintLabels(false);
250     slider.setPaintTicks(true);
251     slider.setBackground(Color.white);
252     slider.setEnabled(false);
253     slider.setOpaque(false);
254     slider.setPreferredSize(new Dimension(100, 32));
255     slider.setToolTipText("Adjust threshold");
256     thresholdValue.setEnabled(false);
257     thresholdValue.setColumns(7);
258     jPanel3.setBackground(Color.white);
259     thresholdIsMin.setBackground(Color.white);
260     thresholdIsMin.setText("Threshold is Min/Max");
261     thresholdIsMin.setToolTipText("Toggle between absolute and relative display threshold.");
262     thresholdIsMin.addActionListener(new ActionListener()
263     {
264       public void actionPerformed(ActionEvent actionEvent)
265       {
266         thresholdIsMin_actionPerformed(actionEvent);
267       }
268     });
269     colourByLabel.setBackground(Color.white);
270     colourByLabel.setText("Colour by Label");
271     colourByLabel.setToolTipText("Display features of the same type with a different label using a different colour. (e.g. domain features)");
272     colourByLabel.addActionListener(new ActionListener()
273     {
274       public void actionPerformed(ActionEvent actionEvent)
275       {
276         colourByLabel_actionPerformed(actionEvent);
277       }
278     });
279     jPanel1.add(ok);
280     jPanel1.add(cancel);
281     jPanel2.add(colourByLabel,java.awt.BorderLayout.WEST);
282     jPanel2.add(colourPanel,java.awt.BorderLayout.EAST);
283     colourPanel.add(minColour);
284     colourPanel.add(maxColour);
285     this.add(jPanel3, java.awt.BorderLayout.CENTER);
286     jPanel3.add(threshold);
287     jPanel3.add(slider);
288     jPanel3.add(thresholdValue);
289     jPanel3.add(thresholdIsMin);
290     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
291     this.add(jPanel2, java.awt.BorderLayout.NORTH);
292   }
293
294
295   JPanel minColour = new JPanel();
296
297   JPanel maxColour = new JPanel();
298
299   JButton ok = new JButton();
300
301   JButton cancel = new JButton();
302   JPanel colourPanel = new JPanel();
303   JPanel jPanel1 = new JPanel();
304
305   JPanel jPanel2 = new JPanel();
306
307   BorderLayout borderLayout1 = new BorderLayout();
308
309   JComboBox threshold = new JComboBox();
310
311   FlowLayout flowLayout1 = new FlowLayout();
312
313   JPanel jPanel3 = new JPanel();
314
315   FlowLayout flowLayout2 = new FlowLayout();
316
317   JSlider slider = new JSlider();
318
319   JTextField thresholdValue = new JTextField(20);
320   // TODO implement GUI for tolower flag
321   // JCheckBox toLower = new JCheckBox();
322
323   JCheckBox thresholdIsMin = new JCheckBox();
324   JCheckBox colourByLabel = new JCheckBox();
325
326   private GraphLine threshline;
327
328
329   private Color oldmaxColour;
330
331
332   private Color oldminColour;
333
334   public void minColour_actionPerformed()
335   {
336     Color col = JColorChooser.showDialog(this,
337             "Select Colour for Minimum Value", minColour.getBackground());
338     if (col != null)
339     {
340       minColour.setBackground(col);
341     }
342     minColour.repaint();
343     changeColour();
344   }
345
346   public void maxColour_actionPerformed()
347   {
348     Color col = JColorChooser.showDialog(this,
349             "Select Colour for Maximum Value", maxColour.getBackground());
350     if (col != null)
351     {
352       maxColour.setBackground(col);
353     }
354     maxColour.repaint();
355     changeColour();
356   }
357
358   void changeColour()
359   {
360     // Check if combobox is still adjusting
361     if (adjusting)
362     {
363       return;
364     }
365
366
367     int aboveThreshold = AnnotationColourGradient.NO_THRESHOLD;
368     if (threshold.getSelectedItem().equals("Above Threshold"))
369     {
370       aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
371     }
372     else if (threshold.getSelectedItem().equals("Below Threshold"))
373     {
374       aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
375     } 
376
377     slider.setEnabled(true);
378     thresholdValue.setEnabled(true);
379     
380     GraduatedColor acg;
381     if (cs.isColourByLabel())
382       {
383         acg = new GraduatedColor(oldminColour, oldmaxColour, min, max);
384       } else {
385         acg = new GraduatedColor(oldminColour=minColour.getBackground(), oldmaxColour=maxColour.getBackground(), min, max);
386         
387       }
388
389     if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
390     {
391       slider.setEnabled(false);
392       thresholdValue.setEnabled(false);
393       thresholdValue.setText("");
394     }
395     else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
396             && threshline == null)
397     {
398       // todo visual indication of feature threshold
399       threshline = new jalview.datamodel.GraphLine(
400                       (max - min) / 2f,
401                       "Threshold", Color.black);
402     }
403
404     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
405     {
406       adjusting = true;
407       acg.setThresh(threshline.value);
408
409       float range = max * 1000f
410               - min * 1000f;
411
412       slider.setMinimum((int) (min * 1000));
413       slider.setMaximum((int) (max * 1000));
414       slider.setValue((int) (threshline.value * 1000));
415       thresholdValue.setText(threshline.value + "");
416       slider.setMajorTickSpacing((int) (range / 10f));
417       slider.setEnabled(true);
418       thresholdValue.setEnabled(true);
419       adjusting = false;
420     }
421
422     acg.setThreshType(aboveThreshold);
423     if (thresholdIsMin.isSelected() && aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
424     {
425       acg.setAutoScaled(false);
426       if (aboveThreshold==AnnotationColourGradient.ABOVE_THRESHOLD)
427       { 
428         acg = new GraduatedColor(acg, threshline.value, max);
429       } else { 
430         acg = new GraduatedColor(acg, min,threshline.value);
431       }
432     } else {
433       acg.setAutoScaled(true);
434     }
435     acg.setColourByLabel(colourByLabel.isSelected());
436     if (acg.isColourByLabel())
437     {
438       maxColour.setEnabled(false);
439       minColour.setEnabled(false);
440       maxColour.setBackground(this.getBackground());
441       minColour.setBackground(this.getBackground());
442     } else {
443       maxColour.setEnabled(true);
444       minColour.setEnabled(true);
445       maxColour.setBackground(oldmaxColour);
446       minColour.setBackground(oldminColour);
447     }
448     fr.featureColours.put(type,acg);
449     cs = acg;
450     ap.paintAlignment(false);
451   }
452   private void raiseClosed() {
453     if (this.colourEditor!=null)
454     {
455       colourEditor.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
456     }
457   }
458   public void ok_actionPerformed(ActionEvent e)
459   {
460     changeColour();
461     try
462     {
463       frame.dispose();
464       raiseClosed();
465     } catch (Exception ex)
466     {
467     }
468   }
469
470   public void cancel_actionPerformed(ActionEvent e)
471   {
472     reset();
473     try
474     {
475       frame.dispose();
476 //      frame.setClosed(true);
477       raiseClosed();
478     } catch (Exception ex)
479     {
480     }
481   }
482
483   void reset()
484   {
485     fr.featureColours.put(type, oldcs);
486     ap.paintAlignment(false);
487     cs = null;
488   }
489
490   public void thresholdCheck_actionPerformed(ActionEvent e)
491   {
492     changeColour();
493   }
494
495   public void annotations_actionPerformed(ActionEvent e)
496   {
497     changeColour();
498   }
499
500   public void threshold_actionPerformed(ActionEvent e)
501   {
502     changeColour();
503   }
504
505   public void thresholdValue_actionPerformed(ActionEvent e)
506   {
507     try
508     {
509       float f = Float.parseFloat(thresholdValue.getText());
510       slider.setValue((int) (f * 1000));
511       threshline.value = f;
512     } catch (NumberFormatException ex)
513     {
514     }
515   }
516
517   public void valueChanged()
518   {
519     threshline.value = (float) slider.getValue() / 1000f;
520     cs.setThresh(threshline.value);
521     changeColour();
522     ap.paintAlignment(false);
523   }
524
525   public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
526   {
527     changeColour();
528   }
529   public void colourByLabel_actionPerformed(ActionEvent actionEvent)
530   {
531     changeColour();
532   }
533   ActionListener colourEditor=null;
534   public void addActionListener(ActionListener graduatedColorEditor)
535   {
536     if (colourEditor!=null)
537     {
538       System.err.println("IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
539     }
540     colourEditor = graduatedColorEditor;
541   }
542
543 }