JAL-1114 - refactor methods handling Vectors and Hashtables to Lists and Maps, and...
[jalview.git] / src / jalview / gui / AnnotationColourChooser.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
25 import javax.swing.*;
26 import javax.swing.event.*;
27
28 import net.miginfocom.swing.MigLayout;
29
30 import jalview.bin.Cache;
31 import jalview.datamodel.*;
32 import jalview.schemes.*;
33 import java.awt.Dimension;
34
35 public class AnnotationColourChooser extends JPanel
36 {
37   JInternalFrame frame;
38
39   AlignViewport av;
40
41   AlignmentPanel ap;
42
43   ColourSchemeI oldcs;
44
45   Hashtable oldgroupColours;
46
47   jalview.datamodel.AlignmentAnnotation currentAnnotation;
48
49   boolean adjusting = false;
50
51   public AnnotationColourChooser(AlignViewport av, final AlignmentPanel ap)
52   {
53     oldcs = av.getGlobalColourScheme();
54     if (av.getAlignment().getGroups() != null)
55     {
56       oldgroupColours = new Hashtable();
57       for (SequenceGroup sg:ap.av.getAlignment().getGroups())
58       {
59         if (sg.cs != null)
60         {
61           oldgroupColours.put(sg, sg.cs);
62         }
63       }
64     }
65     this.av = av;
66     this.ap = ap;
67     frame = new JInternalFrame();
68     frame.setContentPane(this);
69     frame.setLayer(JLayeredPane.PALETTE_LAYER);
70     Desktop.addInternalFrame(frame, "Colour by Annotation", 520, 215);
71
72     slider.addChangeListener(new ChangeListener()
73     {
74       public void stateChanged(ChangeEvent evt)
75       {
76         if (!adjusting)
77         {
78           thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
79           valueChanged();
80         }
81       }
82     });
83     slider.addMouseListener(new MouseAdapter()
84     {
85       public void mouseReleased(MouseEvent evt)
86       {
87         ap.paintAlignment(true);
88       }
89     });
90
91     if (av.getAlignment().getAlignmentAnnotation() == null)
92     {
93       return;
94     }
95
96     // Always get default shading from preferences.
97     setDefaultMinMax();
98     
99     if (oldcs instanceof AnnotationColourGradient)
100     {
101       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
102       currentColours.setSelected(acg.predefinedColours);
103       if (!acg.predefinedColours)
104       {
105         minColour.setBackground(acg.getMinColour());
106         maxColour.setBackground(acg.getMaxColour());
107       }
108     }
109
110     adjusting = true;
111     Vector list = new Vector();
112     int index = 1;
113     for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
114     {
115       String label = av.getAlignment().getAlignmentAnnotation()[i].label;
116       if (!list.contains(label))
117         list.addElement(label);
118       else
119         list.addElement(label + "_" + (index++));
120     }
121
122     annotations = new JComboBox(list);
123
124     threshold.addItem("No Threshold");
125     threshold.addItem("Above Threshold");
126     threshold.addItem("Below Threshold");
127
128     if (oldcs instanceof AnnotationColourGradient)
129     {
130       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
131       annotations.setSelectedItem(acg.getAnnotation());
132       switch (acg.getAboveThreshold()) {
133       case AnnotationColourGradient.NO_THRESHOLD:
134           threshold.setSelectedItem("No Threshold");
135         break;
136       case AnnotationColourGradient.ABOVE_THRESHOLD:
137           threshold.setSelectedItem("Above Threshold");
138         break;
139       case AnnotationColourGradient.BELOW_THRESHOLD:
140         threshold.setSelectedItem("Below Threshold");
141         break;
142         default:
143           throw new Error("Implementation error: don't know about threshold setting for current AnnotationColourGradient.");
144       }
145       thresholdIsMin.setSelected(acg.thresholdIsMinMax);
146       thresholdValue.setText(""+acg.getAnnotationThreshold());
147     }
148
149     try
150     {
151       jbInit();
152     } catch (Exception ex)
153     {
154     }
155
156     adjusting = false;
157
158     changeColour();
159     validate();
160
161   }
162
163   private void setDefaultMinMax()
164   {
165     minColour.setBackground(Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN", Color.orange));
166     maxColour.setBackground(Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red));
167   }
168
169   public AnnotationColourChooser()
170   {
171     try
172     {
173       jbInit();
174     } catch (Exception ex)
175     {
176       ex.printStackTrace();
177     }
178   }
179
180   private void jbInit() throws Exception
181   {
182     minColour.setFont(JvSwingUtils.getLabelFont());
183     minColour.setBorder(BorderFactory.createEtchedBorder());
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.createEtchedBorder());
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     ok.setOpaque(false);
211     ok.setText("OK");
212     ok.addActionListener(new ActionListener()
213     {
214       public void actionPerformed(ActionEvent e)
215       {
216         ok_actionPerformed(e);
217       }
218     });
219     cancel.setOpaque(false);
220     cancel.setText("Cancel");
221     cancel.addActionListener(new ActionListener()
222     {
223       public void actionPerformed(ActionEvent e)
224       {
225         cancel_actionPerformed(e);
226       }
227     });
228     defColours.setOpaque(false);
229     defColours.setText("Defaults");
230     defColours.setToolTipText("Reset min and max colours to defaults from user preferences.");
231     defColours.addActionListener(new ActionListener()
232     {
233       
234       @Override
235       public void actionPerformed(ActionEvent arg0)
236       {
237         resetColours_actionPerformed(arg0);
238       }
239     });
240     
241     annotations.addActionListener(new ActionListener()
242     {
243       public void actionPerformed(ActionEvent e)
244       {
245         annotations_actionPerformed(e);
246       }
247     });
248     threshold.addActionListener(new ActionListener()
249     {
250       public void actionPerformed(ActionEvent e)
251       {
252         threshold_actionPerformed(e);
253       }
254     });
255     thresholdValue.addActionListener(new ActionListener()
256     {
257       public void actionPerformed(ActionEvent e)
258       {
259         thresholdValue_actionPerformed(e);
260       }
261     });
262     slider.setPaintLabels(false);
263     slider.setPaintTicks(true);
264     slider.setBackground(Color.white);
265     slider.setEnabled(false);
266     slider.setOpaque(false);
267     slider.setPreferredSize(new Dimension(100, 32));
268     thresholdValue.setEnabled(false);
269     thresholdValue.setColumns(7);
270     currentColours.setFont(JvSwingUtils.getLabelFont());
271     currentColours.setOpaque(false);
272     currentColours.setText("Use Original Colours");
273     currentColours.addActionListener(new ActionListener()
274     {
275       public void actionPerformed(ActionEvent e)
276       {
277         currentColours_actionPerformed(e);
278       }
279     });
280     thresholdIsMin.setBackground(Color.white);
281     thresholdIsMin.setFont(JvSwingUtils.getLabelFont());
282     thresholdIsMin.setText("Threshold is Min/Max");
283     thresholdIsMin.addActionListener(new ActionListener()
284     {
285       public void actionPerformed(ActionEvent actionEvent)
286       {
287         thresholdIsMin_actionPerformed(actionEvent);
288       }
289     });
290     this.setLayout(borderLayout1);
291     jPanel2.setLayout(new MigLayout("","[left][center][right]","[][][]"));
292     jPanel1.setBackground(Color.white);
293     jPanel2.setBackground(Color.white);
294
295     jPanel1.add(ok);
296     jPanel1.add(cancel);
297     jPanel2.add(annotations);
298     jPanel2.add(currentColours);
299     JPanel colpanel = new JPanel(new FlowLayout());
300     colpanel.setBackground(Color.white);
301     colpanel.add(minColour);
302     colpanel.add(maxColour);
303     jPanel2.add(colpanel, "wrap");
304     
305     jPanel2.add(threshold);
306     jPanel2.add(defColours,"skip 1, wrap");
307     jPanel2.add(thresholdIsMin);
308     jPanel2.add(slider, "grow");
309     jPanel2.add(thresholdValue, "grow");
310     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
311     this.add(jPanel2, java.awt.BorderLayout.CENTER);
312   }
313
314   protected void resetColours_actionPerformed(ActionEvent arg0)
315   {
316     setDefaultMinMax();
317     changeColour();
318   }
319
320   JComboBox annotations;
321
322   JPanel minColour = new JPanel();
323
324   JPanel maxColour = new JPanel();
325   JButton defColours = new JButton();
326   JButton ok = new JButton();
327
328   JButton cancel = new JButton();
329
330   JPanel jPanel1 = new JPanel();
331   JPanel jPanel2 = new JPanel();
332   
333   BorderLayout borderLayout1 = new BorderLayout();
334
335   JComboBox threshold = new JComboBox();
336
337
338   JSlider slider = new JSlider();
339
340   JTextField thresholdValue = new JTextField(20);
341
342   JCheckBox currentColours = new JCheckBox();
343
344   JCheckBox thresholdIsMin = new JCheckBox();
345
346   public void minColour_actionPerformed()
347   {
348     Color col = JColorChooser.showDialog(this,
349             "Select Colour for Minimum Value", minColour.getBackground());
350     if (col != null)
351     {
352       minColour.setBackground(col);
353     }
354     minColour.repaint();
355     changeColour();
356   }
357
358   public void maxColour_actionPerformed()
359   {
360     Color col = JColorChooser.showDialog(this,
361             "Select Colour for Maximum Value", maxColour.getBackground());
362     if (col != null)
363     {
364       maxColour.setBackground(col);
365     }
366     maxColour.repaint();
367     changeColour();
368   }
369
370   void changeColour()
371   {
372     // Check if combobox is still adjusting
373     if (adjusting)
374     {
375       return;
376     }
377
378     currentAnnotation = av.getAlignment().getAlignmentAnnotation()[annotations
379             .getSelectedIndex()];
380
381     int aboveThreshold = -1;
382     if (threshold.getSelectedItem().equals("Above Threshold"))
383     {
384       aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
385     }
386     else if (threshold.getSelectedItem().equals("Below Threshold"))
387     {
388       aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
389     }
390
391     slider.setEnabled(true);
392     thresholdValue.setEnabled(true);
393     thresholdIsMin.setEnabled(true);
394
395     if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
396     {
397       slider.setEnabled(false);
398       thresholdValue.setEnabled(false);
399       thresholdValue.setText("");
400       thresholdIsMin.setEnabled(false);
401     }
402     else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
403             && currentAnnotation.threshold == null)
404     {
405       currentAnnotation
406               .setThreshold(new jalview.datamodel.GraphLine(
407                       (currentAnnotation.graphMax - currentAnnotation.graphMin) / 2f,
408                       "Threshold", Color.black));
409     }
410
411     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
412     {
413       adjusting = true;
414       float range = currentAnnotation.graphMax * 1000
415               - currentAnnotation.graphMin * 1000;
416
417       slider.setMinimum((int) (currentAnnotation.graphMin * 1000));
418       slider.setMaximum((int) (currentAnnotation.graphMax * 1000));
419       slider.setValue((int) (currentAnnotation.threshold.value * 1000));
420       thresholdValue.setText(currentAnnotation.threshold.value + "");
421       slider.setMajorTickSpacing((int) (range / 10f));
422       slider.setEnabled(true);
423       thresholdValue.setEnabled(true);
424       adjusting = false;
425     }
426
427     AnnotationColourGradient acg = null;
428     if (currentColours.isSelected())
429     {
430       acg = new AnnotationColourGradient(currentAnnotation,
431               av.getGlobalColourScheme(), aboveThreshold);
432     }
433     else
434     {
435       acg = new AnnotationColourGradient(currentAnnotation,
436               minColour.getBackground(), maxColour.getBackground(),
437               aboveThreshold);
438     }
439
440     if (currentAnnotation.graphMin == 0f
441             && currentAnnotation.graphMax == 0f)
442     {
443       acg.predefinedColours = true;
444     }
445
446     acg.thresholdIsMinMax = thresholdIsMin.isSelected();
447
448     av.setGlobalColourScheme(acg);
449
450     if (av.getAlignment().getGroups() != null)
451     {
452       
453       for (SequenceGroup sg:ap.av.getAlignment().getGroups())
454       {
455         if (sg.cs == null)
456         {
457           continue;
458         }
459
460         if (currentColours.isSelected())
461         {
462           sg.cs = new AnnotationColourGradient(currentAnnotation, sg.cs,
463                   aboveThreshold);
464         }
465         else
466         {
467           sg.cs = new AnnotationColourGradient(currentAnnotation,
468                   minColour.getBackground(), maxColour.getBackground(),
469                   aboveThreshold);
470         }
471
472       }
473     }
474     // ensure all associated views (overviews, structures, etc) are notified of updated colours.
475     ap.paintAlignment(true);
476   }
477
478   public void ok_actionPerformed(ActionEvent e)
479   {
480     changeColour();
481     try
482     {
483       frame.setClosed(true);
484     } catch (Exception ex)
485     {
486     }
487   }
488
489   public void cancel_actionPerformed(ActionEvent e)
490   {
491     reset();
492     // ensure all original colouring is propagated to listeners. 
493     ap.paintAlignment(true);
494     try
495     {
496       frame.setClosed(true);
497     } catch (Exception ex)
498     {
499     }
500   }
501
502   void reset()
503   {
504     av.setGlobalColourScheme(oldcs);
505     if (av.getAlignment().getGroups() != null)
506     {
507       
508       for (SequenceGroup sg:ap.av.getAlignment().getGroups())
509       {
510         sg.cs = (ColourSchemeI) oldgroupColours.get(sg);
511       }
512     }
513   }
514
515   public void thresholdCheck_actionPerformed(ActionEvent e)
516   {
517     changeColour();
518   }
519
520   public void annotations_actionPerformed(ActionEvent e)
521   {
522     changeColour();
523   }
524
525   public void threshold_actionPerformed(ActionEvent e)
526   {
527     changeColour();
528   }
529
530   public void thresholdValue_actionPerformed(ActionEvent e)
531   {
532     try
533     {
534       float f = Float.parseFloat(thresholdValue.getText());
535       slider.setValue((int) (f * 1000));
536     } catch (NumberFormatException ex)
537     {
538     }
539   }
540
541   public void valueChanged()
542   {
543     if (currentColours.isSelected()
544             && !(av.getGlobalColourScheme() instanceof AnnotationColourGradient))
545     {
546       changeColour();
547     }
548
549     currentAnnotation.threshold.value = (float) slider.getValue() / 1000f;
550     ap.paintAlignment(false);
551   }
552
553   public void currentColours_actionPerformed(ActionEvent e)
554   {
555     if (currentColours.isSelected())
556     {
557       reset();
558     }
559
560     maxColour.setEnabled(!currentColours.isSelected());
561     minColour.setEnabled(!currentColours.isSelected());
562
563     changeColour();
564   }
565
566   public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
567   {
568     changeColour();
569   }
570
571 }