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