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