1fb53e42004e46ca3025ae9f3d39942940855f92
[jalview.git] / src / jalview / gui / FeatureColourChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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 "
79             + type, 480, 185);
80     // frame.setLayer(JLayeredPane.PALETTE_LAYER);
81     // Desktop.addInternalFrame(frame, "Graduated Feature Colour for "+type,
82     // 480, 145);
83
84     slider.addChangeListener(new ChangeListener()
85     {
86       public void stateChanged(ChangeEvent evt)
87       {
88         if (!adjusting)
89         {
90           thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
91           valueChanged();
92         }
93       }
94     });
95     slider.addMouseListener(new MouseAdapter()
96     {
97       public void mouseReleased(MouseEvent evt)
98       {
99         if (ap != null)
100         {
101           ap.paintAlignment(true);
102         }
103         ;
104       }
105     });
106
107     float mm[] = ((float[][]) fr.minmax.get(type))[0];
108     min = mm[0];
109     max = mm[1];
110     oldcs = fr.featureColours.get(type);
111     if (oldcs instanceof GraduatedColor)
112     {
113       if (((GraduatedColor) oldcs).isAutoScale())
114       {
115         // update the scale
116         cs = new GraduatedColor((GraduatedColor) oldcs, min, max);
117       }
118       else
119       {
120         cs = new GraduatedColor((GraduatedColor) oldcs);
121       }
122     }
123     else
124     {
125       // promote original color to a graduated color
126       Color bl = Color.black;
127       if (oldcs instanceof Color)
128       {
129         bl = (Color) oldcs;
130       }
131       // original colour becomes the maximum colour
132       cs = new GraduatedColor(Color.white, bl, mm[0], mm[1]);
133       cs.setColourByLabel(false);
134     }
135     minColour.setBackground(oldminColour = cs.getMinColor());
136     maxColour.setBackground(oldmaxColour = cs.getMaxColor());
137     adjusting = true;
138
139     try
140     {
141       jbInit();
142     } catch (Exception ex)
143     {
144     }
145     // update the gui from threshold state
146     thresholdIsMin.setSelected(!cs.isAutoScale());
147     colourByLabel.setSelected(cs.isColourByLabel());
148     if (cs.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
149     {
150       // initialise threshold slider and selector
151       threshold
152               .setSelectedIndex(cs.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD ? 1
153                       : 2);
154       slider.setEnabled(true);
155       thresholdValue.setEnabled(true);
156       threshline = new jalview.datamodel.GraphLine((max - min) / 2f,
157               "Threshold", Color.black);
158
159     }
160
161     adjusting = false;
162
163     changeColour();
164     waitForInput();
165   }
166
167   public FeatureColourChooser()
168   {
169     try
170     {
171       jbInit();
172     } catch (Exception ex)
173     {
174       ex.printStackTrace();
175     }
176   }
177
178   private void jbInit() throws Exception
179   {
180
181     minColour.setFont(JvSwingUtils.getLabelFont());
182     minColour.setBorder(BorderFactory.createLineBorder(Color.black));
183     minColour.setPreferredSize(new Dimension(40, 20));
184     minColour.setToolTipText("Minimum Colour");
185     minColour.addMouseListener(new MouseAdapter()
186     {
187       public void mousePressed(MouseEvent e)
188       {
189         if (minColour.isEnabled())
190         {
191           minColour_actionPerformed();
192         }
193       }
194     });
195     maxColour.setFont(JvSwingUtils.getLabelFont());
196     maxColour.setBorder(BorderFactory.createLineBorder(Color.black));
197     maxColour.setPreferredSize(new Dimension(40, 20));
198     maxColour.setToolTipText("Maximum Colour");
199     maxColour.addMouseListener(new MouseAdapter()
200     {
201       public void mousePressed(MouseEvent e)
202       {
203         if (maxColour.isEnabled())
204         {
205           maxColour_actionPerformed();
206         }
207       }
208     });
209     maxColour.setBorder(new LineBorder(Color.black));
210     minText.setText("Min:");
211     minText.setFont(JvSwingUtils.getLabelFont());
212     maxText.setText("Max:");
213     maxText.setFont(JvSwingUtils.getLabelFont());
214     this.setLayout(borderLayout1);
215     jPanel2.setLayout(flowLayout1);
216     jPanel1.setBackground(Color.white);
217     jPanel2.setBackground(Color.white);
218     threshold.addActionListener(new ActionListener()
219     {
220       public void actionPerformed(ActionEvent e)
221       {
222         threshold_actionPerformed(e);
223       }
224     });
225     threshold.setToolTipText("Threshold the feature display by score.");
226     threshold.addItem("No Threshold"); // index 0
227     threshold.addItem("Above Threshold"); // index 1
228     threshold.addItem("Below Threshold"); // index 2
229     jPanel3.setLayout(flowLayout2);
230     thresholdValue.addActionListener(new ActionListener()
231     {
232       public void actionPerformed(ActionEvent e)
233       {
234         thresholdValue_actionPerformed(e);
235       }
236     });
237     slider.setPaintLabels(false);
238     slider.setPaintTicks(true);
239     slider.setBackground(Color.white);
240     slider.setEnabled(false);
241     slider.setOpaque(false);
242     slider.setPreferredSize(new Dimension(100, 32));
243     slider.setToolTipText("Adjust threshold");
244     thresholdValue.setEnabled(false);
245     thresholdValue.setColumns(7);
246     jPanel3.setBackground(Color.white);
247     thresholdIsMin.setBackground(Color.white);
248     thresholdIsMin.setText("Threshold is Min/Max");
249     thresholdIsMin
250             .setToolTipText("Toggle between absolute and relative display threshold.");
251     thresholdIsMin.addActionListener(new ActionListener()
252     {
253       public void actionPerformed(ActionEvent actionEvent)
254       {
255         thresholdIsMin_actionPerformed(actionEvent);
256       }
257     });
258     colourByLabel.setBackground(Color.white);
259     colourByLabel.setText("Colour by Label");
260     colourByLabel
261             .setToolTipText("Display features of the same type with a different label using a different colour. (e.g. domain features)");
262     colourByLabel.addActionListener(new ActionListener()
263     {
264       public void actionPerformed(ActionEvent actionEvent)
265       {
266         colourByLabel_actionPerformed(actionEvent);
267       }
268     });
269     colourPanel.setBackground(Color.white);
270     jPanel1.add(ok);
271     jPanel1.add(cancel);
272     jPanel2.add(colourByLabel, java.awt.BorderLayout.WEST);
273     jPanel2.add(colourPanel, java.awt.BorderLayout.EAST);
274     colourPanel.add(minText);
275     colourPanel.add(minColour);
276     colourPanel.add(maxText);
277     colourPanel.add(maxColour);
278     this.add(jPanel3, java.awt.BorderLayout.CENTER);
279     jPanel3.add(threshold);
280     jPanel3.add(slider);
281     jPanel3.add(thresholdValue);
282     jPanel3.add(thresholdIsMin);
283     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
284     this.add(jPanel2, java.awt.BorderLayout.NORTH);
285   }
286
287   JLabel minText = new JLabel();
288
289   JLabel maxText = new JLabel();
290
291   JPanel minColour = new JPanel();
292
293   JPanel maxColour = new JPanel();
294
295   JPanel colourPanel = new JPanel();
296
297   JPanel jPanel1 = new JPanel();
298
299   JPanel jPanel2 = new JPanel();
300
301   BorderLayout borderLayout1 = new BorderLayout();
302
303   JComboBox threshold = new JComboBox();
304
305   FlowLayout flowLayout1 = new FlowLayout();
306
307   JPanel jPanel3 = new JPanel();
308
309   FlowLayout flowLayout2 = new FlowLayout();
310
311   JSlider slider = new JSlider();
312
313   JTextField thresholdValue = new JTextField(20);
314
315   // TODO implement GUI for tolower flag
316   // JCheckBox toLower = new JCheckBox();
317
318   JCheckBox thresholdIsMin = new JCheckBox();
319
320   JCheckBox colourByLabel = new JCheckBox();
321
322   private GraphLine threshline;
323
324   private Color oldmaxColour;
325
326   private Color oldminColour;
327
328   public void minColour_actionPerformed()
329   {
330     Color col = JColorChooser.showDialog(this,
331             "Select Colour for Minimum Value", minColour.getBackground());
332     if (col != null)
333     {
334       minColour.setBackground(col);
335       minColour.setForeground(col);
336     }
337     minColour.repaint();
338     changeColour();
339   }
340
341   public void maxColour_actionPerformed()
342   {
343     Color col = JColorChooser.showDialog(this,
344             "Select Colour for Maximum Value", maxColour.getBackground());
345     if (col != null)
346     {
347       maxColour.setBackground(col);
348       maxColour.setForeground(col);
349     }
350     maxColour.repaint();
351     changeColour();
352   }
353
354   void changeColour()
355   {
356     // Check if combobox is still adjusting
357     if (adjusting)
358     {
359       return;
360     }
361
362     int aboveThreshold = AnnotationColourGradient.NO_THRESHOLD;
363     if (threshold.getSelectedItem().equals("Above Threshold"))
364     {
365       aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
366     }
367     else if (threshold.getSelectedItem().equals("Below Threshold"))
368     {
369       aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
370     }
371
372     slider.setEnabled(true);
373     thresholdValue.setEnabled(true);
374
375     GraduatedColor acg;
376     if (cs.isColourByLabel())
377     {
378       acg = new GraduatedColor(oldminColour, oldmaxColour, min, max);
379     }
380     else
381     {
382       acg = new GraduatedColor(oldminColour = minColour.getBackground(),
383               oldmaxColour = maxColour.getBackground(), min, max);
384
385     }
386
387     if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
388     {
389       slider.setEnabled(false);
390       thresholdValue.setEnabled(false);
391       thresholdValue.setText("");
392       thresholdIsMin.setEnabled(false);
393     }
394     else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
395             && threshline == null)
396     {
397       // todo visual indication of feature threshold
398       threshline = new jalview.datamodel.GraphLine((max - min) / 2f,
399               "Threshold", Color.black);
400     }
401
402     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
403     {
404       adjusting = true;
405       acg.setThresh(threshline.value);
406
407       float range = max * 1000f - min * 1000f;
408
409       slider.setMinimum((int) (min * 1000));
410       slider.setMaximum((int) (max * 1000));
411       slider.setValue((int) (threshline.value * 1000));
412       thresholdValue.setText(threshline.value + "");
413       slider.setMajorTickSpacing((int) (range / 10f));
414       slider.setEnabled(true);
415       thresholdValue.setEnabled(true);
416       thresholdIsMin.setEnabled(!colourByLabel.isSelected());
417       adjusting = false;
418     }
419
420     acg.setThreshType(aboveThreshold);
421     if (thresholdIsMin.isSelected()
422             && aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
423     {
424       acg.setAutoScaled(false);
425       if (aboveThreshold == AnnotationColourGradient.ABOVE_THRESHOLD)
426       {
427         acg = new GraduatedColor(acg, threshline.value, max);
428       }
429       else
430       {
431         acg = new GraduatedColor(acg, min, threshline.value);
432       }
433     }
434     else
435     {
436       acg.setAutoScaled(true);
437     }
438     acg.setColourByLabel(colourByLabel.isSelected());
439     if (acg.isColourByLabel())
440     {
441       maxColour.setEnabled(false);
442       minColour.setEnabled(false);
443       maxColour.setBackground(this.getBackground());
444       maxColour.setForeground(this.getBackground());
445       minColour.setBackground(this.getBackground());
446       minColour.setForeground(this.getBackground());
447
448     }
449     else
450     {
451       maxColour.setEnabled(true);
452       minColour.setEnabled(true);
453       maxColour.setBackground(oldmaxColour);
454       minColour.setBackground(oldminColour);
455       maxColour.setForeground(oldmaxColour);
456       minColour.setForeground(oldminColour);
457     }
458     fr.featureColours.put(type, acg);
459     cs = acg;
460     ap.paintAlignment(false);
461   }
462
463   protected void raiseClosed()
464   {
465     if (this.colourEditor != null)
466     {
467       colourEditor.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
468     }
469   }
470
471   public void okPressed()
472   {
473     changeColour();
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 }