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