formatting
[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       seqAssociated.setSelected(acg.isSeqAssociated());
109     }
110     adjusting = true;
111     annotations = new JComboBox(
112             getAnnotationItems(seqAssociated.isSelected()));
113
114     threshold.addItem("No Threshold");
115     threshold.addItem("Above Threshold");
116     threshold.addItem("Below Threshold");
117
118     if (oldcs instanceof AnnotationColourGradient)
119     {
120       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
121       annotations.setSelectedItem(acg.getAnnotation());
122       switch (acg.getAboveThreshold())
123       {
124       case AnnotationColourGradient.NO_THRESHOLD:
125         threshold.setSelectedItem("No Threshold");
126         break;
127       case AnnotationColourGradient.ABOVE_THRESHOLD:
128         threshold.setSelectedItem("Above Threshold");
129         break;
130       case AnnotationColourGradient.BELOW_THRESHOLD:
131         threshold.setSelectedItem("Below Threshold");
132         break;
133       default:
134         throw new Error(
135                 "Implementation error: don't know about threshold setting for current AnnotationColourGradient.");
136       }
137       thresholdIsMin.setSelected(acg.thresholdIsMinMax);
138       thresholdValue.setText("" + acg.getAnnotationThreshold());
139     }
140
141     try
142     {
143       jbInit();
144     } catch (Exception ex)
145     {
146     }
147
148     adjusting = false;
149
150     changeColour();
151     frame.invalidate();
152     frame.pack();
153
154   }
155
156   private Vector<String> getAnnotationItems(boolean isSeqAssociated)
157   {
158     Vector<String> list = new Vector<String>();
159     int index = 1;
160     int[] anmap = new int[av.getAlignment().getAlignmentAnnotation().length];
161     boolean enableSeqAss = false;
162     for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
163     {
164       if (av.getAlignment().getAlignmentAnnotation()[i].sequenceRef == null)
165       {
166         if (isSeqAssociated)
167         {
168           continue;
169         }
170       }
171       else
172       {
173         enableSeqAss = true;
174       }
175       String label = av.getAlignment().getAlignmentAnnotation()[i].label;
176       if (!list.contains(label))
177       {
178         anmap[list.size()] = i;
179         list.addElement(label);
180
181       }
182       else
183       {
184         if (!isSeqAssociated)
185         {
186           anmap[list.size()] = i;
187           list.addElement(label + "_" + (index++));
188         }
189       }
190     }
191     seqAssociated.setEnabled(enableSeqAss);
192     annmap = new int[list.size()];
193     System.arraycopy(anmap, 0, annmap, 0, annmap.length);
194     return list;
195   }
196
197   private void setDefaultMinMax()
198   {
199     minColour.setBackground(Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN",
200             Color.orange));
201     maxColour.setBackground(Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX",
202             Color.red));
203   }
204
205   public AnnotationColourChooser()
206   {
207     try
208     {
209       jbInit();
210     } catch (Exception ex)
211     {
212       ex.printStackTrace();
213     }
214   }
215
216   private void jbInit() throws Exception
217   {
218     minColour.setFont(JvSwingUtils.getLabelFont());
219     minColour.setBorder(BorderFactory.createEtchedBorder());
220     minColour.setPreferredSize(new Dimension(40, 20));
221     minColour.setToolTipText("Minimum Colour");
222     minColour.addMouseListener(new MouseAdapter()
223     {
224       public void mousePressed(MouseEvent e)
225       {
226         if (minColour.isEnabled())
227         {
228           minColour_actionPerformed();
229         }
230       }
231     });
232     maxColour.setFont(JvSwingUtils.getLabelFont());
233     maxColour.setBorder(BorderFactory.createEtchedBorder());
234     maxColour.setPreferredSize(new Dimension(40, 20));
235     maxColour.setToolTipText("Maximum Colour");
236     maxColour.addMouseListener(new MouseAdapter()
237     {
238       public void mousePressed(MouseEvent e)
239       {
240         if (maxColour.isEnabled())
241         {
242           maxColour_actionPerformed();
243         }
244       }
245     });
246     ok.setOpaque(false);
247     ok.setText("OK");
248     ok.addActionListener(new ActionListener()
249     {
250       public void actionPerformed(ActionEvent e)
251       {
252         ok_actionPerformed(e);
253       }
254     });
255     cancel.setOpaque(false);
256     cancel.setText("Cancel");
257     cancel.addActionListener(new ActionListener()
258     {
259       public void actionPerformed(ActionEvent e)
260       {
261         cancel_actionPerformed(e);
262       }
263     });
264     defColours.setOpaque(false);
265     defColours.setText("Defaults");
266     defColours
267             .setToolTipText("Reset min and max colours to defaults from user preferences.");
268     defColours.addActionListener(new ActionListener()
269     {
270
271       @Override
272       public void actionPerformed(ActionEvent arg0)
273       {
274         resetColours_actionPerformed(arg0);
275       }
276     });
277
278     annotations.addActionListener(new ActionListener()
279     {
280       public void actionPerformed(ActionEvent e)
281       {
282         annotations_actionPerformed(e);
283       }
284     });
285     threshold.addActionListener(new ActionListener()
286     {
287       public void actionPerformed(ActionEvent e)
288       {
289         threshold_actionPerformed(e);
290       }
291     });
292     thresholdValue.addActionListener(new ActionListener()
293     {
294       public void actionPerformed(ActionEvent e)
295       {
296         thresholdValue_actionPerformed(e);
297       }
298     });
299     slider.setPaintLabels(false);
300     slider.setPaintTicks(true);
301     slider.setBackground(Color.white);
302     slider.setEnabled(false);
303     slider.setOpaque(false);
304     slider.setPreferredSize(new Dimension(100, 32));
305     thresholdValue.setEnabled(false);
306     thresholdValue.setColumns(7);
307     currentColours.setFont(JvSwingUtils.getLabelFont());
308     currentColours.setOpaque(false);
309     currentColours.setText("Use Original Colours");
310     currentColours.addActionListener(new ActionListener()
311     {
312       public void actionPerformed(ActionEvent e)
313       {
314         currentColours_actionPerformed(e);
315       }
316     });
317     thresholdIsMin.setBackground(Color.white);
318     thresholdIsMin.setFont(JvSwingUtils.getLabelFont());
319     thresholdIsMin.setText("Threshold is Min/Max");
320     thresholdIsMin.addActionListener(new ActionListener()
321     {
322       public void actionPerformed(ActionEvent actionEvent)
323       {
324         thresholdIsMin_actionPerformed(actionEvent);
325       }
326     });
327     seqAssociated.setBackground(Color.white);
328     seqAssociated.setFont(JvSwingUtils.getLabelFont());
329     seqAssociated.setText("Per-sequence only");
330     seqAssociated.addActionListener(new ActionListener()
331     {
332
333       @Override
334       public void actionPerformed(ActionEvent arg0)
335       {
336         seqAssociated_actionPerformed(arg0);
337       }
338     });
339
340     this.setLayout(borderLayout1);
341     jPanel2.setLayout(new MigLayout("", "[left][center][right]", "[][][]"));
342     jPanel1.setBackground(Color.white);
343     jPanel2.setBackground(Color.white);
344
345     jPanel1.add(ok);
346     jPanel1.add(cancel);
347     jPanel2.add(annotations, "grow, wrap");
348     jPanel2.add(seqAssociated);
349     jPanel2.add(currentColours);
350     JPanel colpanel = new JPanel(new FlowLayout());
351     colpanel.setBackground(Color.white);
352     colpanel.add(minColour);
353     colpanel.add(maxColour);
354     jPanel2.add(colpanel, "wrap");
355     jPanel2.add(threshold);
356     jPanel2.add(defColours, "skip 1, wrap");
357     jPanel2.add(thresholdIsMin);
358     jPanel2.add(slider, "grow");
359     jPanel2.add(thresholdValue, "grow");
360     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
361     this.add(jPanel2, java.awt.BorderLayout.CENTER);
362     this.validate();
363   }
364
365   protected void seqAssociated_actionPerformed(ActionEvent arg0)
366   {
367     adjusting = true;
368     String cursel = (String) annotations.getSelectedItem();
369     boolean isvalid = false, isseqs = seqAssociated.isSelected();
370     this.annotations.removeAllItems();
371     for (String anitem : getAnnotationItems(seqAssociated.isSelected()))
372     {
373       if (anitem.equals(cursel) || (isseqs && cursel.startsWith(anitem)))
374       {
375         isvalid = true;
376         cursel = anitem;
377       }
378       this.annotations.addItem(anitem);
379     }
380     adjusting = false;
381     if (isvalid)
382     {
383       this.annotations.setSelectedItem(cursel);
384     }
385     else
386     {
387       if (annotations.getItemCount() > 0)
388       {
389         annotations.setSelectedIndex(0);
390       }
391     }
392   }
393
394   protected void resetColours_actionPerformed(ActionEvent arg0)
395   {
396     setDefaultMinMax();
397     changeColour();
398   }
399
400   JComboBox annotations;
401
402   int[] annmap;
403
404   JPanel minColour = new JPanel();
405
406   JPanel maxColour = new JPanel();
407
408   JButton defColours = new JButton();
409
410   JButton ok = new JButton();
411
412   JButton cancel = new JButton();
413
414   JPanel jPanel1 = new JPanel();
415
416   JPanel jPanel2 = new JPanel();
417
418   BorderLayout borderLayout1 = new BorderLayout();
419
420   JComboBox threshold = new JComboBox();
421
422   JSlider slider = new JSlider();
423
424   JTextField thresholdValue = new JTextField(20);
425
426   JCheckBox currentColours = new JCheckBox();
427
428   JCheckBox thresholdIsMin = new JCheckBox();
429
430   JCheckBox seqAssociated = new JCheckBox();
431
432   public void minColour_actionPerformed()
433   {
434     Color col = JColorChooser.showDialog(this,
435             "Select Colour for Minimum Value", minColour.getBackground());
436     if (col != null)
437     {
438       minColour.setBackground(col);
439     }
440     minColour.repaint();
441     changeColour();
442   }
443
444   public void maxColour_actionPerformed()
445   {
446     Color col = JColorChooser.showDialog(this,
447             "Select Colour for Maximum Value", maxColour.getBackground());
448     if (col != null)
449     {
450       maxColour.setBackground(col);
451     }
452     maxColour.repaint();
453     changeColour();
454   }
455
456   void changeColour()
457   {
458     // Check if combobox is still adjusting
459     if (adjusting)
460     {
461       return;
462     }
463
464     currentAnnotation = av.getAlignment().getAlignmentAnnotation()[annmap[annotations
465             .getSelectedIndex()]];
466
467     int aboveThreshold = -1;
468     if (threshold.getSelectedItem().equals("Above Threshold"))
469     {
470       aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
471     }
472     else if (threshold.getSelectedItem().equals("Below Threshold"))
473     {
474       aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
475     }
476
477     slider.setEnabled(true);
478     thresholdValue.setEnabled(true);
479     thresholdIsMin.setEnabled(true);
480
481     if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
482     {
483       slider.setEnabled(false);
484       thresholdValue.setEnabled(false);
485       thresholdValue.setText("");
486       thresholdIsMin.setEnabled(false);
487     }
488     else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
489             && currentAnnotation.threshold == null)
490     {
491       currentAnnotation
492               .setThreshold(new jalview.datamodel.GraphLine(
493                       (currentAnnotation.graphMax - currentAnnotation.graphMin) / 2f,
494                       "Threshold", Color.black));
495     }
496
497     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
498     {
499       adjusting = true;
500       float range = currentAnnotation.graphMax * 1000
501               - currentAnnotation.graphMin * 1000;
502
503       slider.setMinimum((int) (currentAnnotation.graphMin * 1000));
504       slider.setMaximum((int) (currentAnnotation.graphMax * 1000));
505       slider.setValue((int) (currentAnnotation.threshold.value * 1000));
506       thresholdValue.setText(currentAnnotation.threshold.value + "");
507       slider.setMajorTickSpacing((int) (range / 10f));
508       slider.setEnabled(true);
509       thresholdValue.setEnabled(true);
510       adjusting = false;
511     }
512
513     AnnotationColourGradient acg = null;
514     if (currentColours.isSelected())
515     {
516       acg = new AnnotationColourGradient(currentAnnotation,
517               av.getGlobalColourScheme(), aboveThreshold);
518     }
519     else
520     {
521       acg = new AnnotationColourGradient(currentAnnotation,
522               minColour.getBackground(), maxColour.getBackground(),
523               aboveThreshold);
524     }
525     acg.setSeqAssociated(seqAssociated.isSelected());
526
527     if (currentAnnotation.graphMin == 0f
528             && currentAnnotation.graphMax == 0f)
529     {
530       acg.predefinedColours = true;
531     }
532
533     acg.thresholdIsMinMax = thresholdIsMin.isSelected();
534
535     av.setGlobalColourScheme(acg);
536
537     if (av.getAlignment().getGroups() != null)
538     {
539
540       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
541       {
542         if (sg.cs == null)
543         {
544           continue;
545         }
546
547         if (currentColours.isSelected())
548         {
549           sg.cs = new AnnotationColourGradient(currentAnnotation, sg.cs,
550                   aboveThreshold);
551           ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
552                   .isSelected());
553
554         }
555         else
556         {
557           sg.cs = new AnnotationColourGradient(currentAnnotation,
558                   minColour.getBackground(), maxColour.getBackground(),
559                   aboveThreshold);
560           ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
561                   .isSelected());
562         }
563
564       }
565     }
566     ap.alignmentChanged();
567     // ensure all associated views (overviews, structures, etc) are notified of
568     // updated colours.
569     ap.paintAlignment(true);
570   }
571
572   public void ok_actionPerformed(ActionEvent e)
573   {
574     changeColour();
575     try
576     {
577       frame.setClosed(true);
578     } catch (Exception ex)
579     {
580     }
581   }
582
583   public void cancel_actionPerformed(ActionEvent e)
584   {
585     reset();
586     // ensure all original colouring is propagated to listeners.
587     ap.paintAlignment(true);
588     try
589     {
590       frame.setClosed(true);
591     } catch (Exception ex)
592     {
593     }
594   }
595
596   void reset()
597   {
598     av.setGlobalColourScheme(oldcs);
599     if (av.getAlignment().getGroups() != null)
600     {
601
602       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
603       {
604         sg.cs = (ColourSchemeI) oldgroupColours.get(sg);
605       }
606     }
607   }
608
609   public void thresholdCheck_actionPerformed(ActionEvent e)
610   {
611     changeColour();
612   }
613
614   public void annotations_actionPerformed(ActionEvent e)
615   {
616     changeColour();
617   }
618
619   public void threshold_actionPerformed(ActionEvent e)
620   {
621     changeColour();
622   }
623
624   public void thresholdValue_actionPerformed(ActionEvent e)
625   {
626     try
627     {
628       float f = Float.parseFloat(thresholdValue.getText());
629       slider.setValue((int) (f * 1000));
630     } catch (NumberFormatException ex)
631     {
632     }
633   }
634
635   public void valueChanged()
636   {
637     if (currentColours.isSelected()
638             && !(av.getGlobalColourScheme() instanceof AnnotationColourGradient))
639     {
640       changeColour();
641     }
642
643     currentAnnotation.threshold.value = (float) slider.getValue() / 1000f;
644     ap.paintAlignment(false);
645   }
646
647   public void currentColours_actionPerformed(ActionEvent e)
648   {
649     if (currentColours.isSelected())
650     {
651       reset();
652     }
653
654     maxColour.setEnabled(!currentColours.isSelected());
655     minColour.setEnabled(!currentColours.isSelected());
656
657     changeColour();
658   }
659
660   public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
661   {
662     changeColour();
663   }
664
665 }