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