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