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