JAL-3083 reset threshold on Cancel; Javadoc
[jalview.git] / src / jalview / gui / AnnotationColourChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.AlignmentAnnotation;
25 import jalview.datamodel.GraphLine;
26 import jalview.datamodel.SequenceGroup;
27 import jalview.schemes.AnnotationColourGradient;
28 import jalview.schemes.ColourSchemeI;
29 import jalview.util.MessageManager;
30
31 import java.awt.BorderLayout;
32 import java.awt.Color;
33 import java.awt.Dimension;
34 import java.awt.FlowLayout;
35 import java.awt.event.ActionEvent;
36 import java.awt.event.ActionListener;
37 import java.awt.event.MouseAdapter;
38 import java.awt.event.MouseEvent;
39 import java.util.HashMap;
40 import java.util.Map;
41 import java.util.Map.Entry;
42 import java.util.Vector;
43
44 import javax.swing.BorderFactory;
45 import javax.swing.JButton;
46 import javax.swing.JCheckBox;
47 import javax.swing.JColorChooser;
48 import javax.swing.JComboBox;
49 import javax.swing.JInternalFrame;
50 import javax.swing.JLayeredPane;
51 import javax.swing.JPanel;
52
53 import net.miginfocom.swing.MigLayout;
54
55 @SuppressWarnings("serial")
56 public class AnnotationColourChooser extends AnnotationRowFilter
57 {
58   private static final int ONETHOUSAND = 1000;
59
60   private ColourSchemeI oldcs;
61
62   private JButton defColours;
63
64   private Map<SequenceGroup, ColourSchemeI> oldgroupColours;
65
66   private Map<AlignmentAnnotation, GraphLine> oldThresholds;
67
68   private JCheckBox useOriginalColours = new JCheckBox();
69
70   private JPanel minColour = new JPanel();
71
72   private JPanel maxColour = new JPanel();
73
74   private JCheckBox thresholdIsMin = new JCheckBox();
75
76   protected static final int MIN_WIDTH = 500;
77
78   protected static final int MIN_HEIGHT = 240;
79
80   /**
81    * Constructor
82    * 
83    * @param av
84    * @param ap
85    */
86   public AnnotationColourChooser(AlignViewport av, final AlignmentPanel ap)
87   {
88     super(av, ap);
89
90     saveInitialState();
91
92     frame = new JInternalFrame();
93     frame.setContentPane(this);
94     frame.setLayer(JLayeredPane.PALETTE_LAYER);
95     Desktop.addInternalFrame(frame,
96             MessageManager.getString("label.colour_by_annotation"), 520,
97             215);
98     frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
99     addSliderChangeListener();
100     addSliderMouseListeners();
101
102     if (av.getAlignment().getAlignmentAnnotation() == null)
103     {
104       return;
105     }
106
107     // Always get default shading from preferences.
108     setDefaultMinMax();
109
110     adjusting = true;
111     if (oldcs instanceof AnnotationColourGradient)
112     {
113       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
114       useOriginalColours.setSelected(
115               acg.isPredefinedColours() || acg.getBaseColour() != null);
116       if (!acg.isPredefinedColours() && acg.getBaseColour() == null)
117       {
118         minColour.setBackground(acg.getMinColour());
119         maxColour.setBackground(acg.getMaxColour());
120       }
121       seqAssociated.setSelected(acg.isSeqAssociated());
122
123     }
124     Vector<String> annotItems = getAnnotationItems(
125             seqAssociated.isSelected());
126     annotations = new JComboBox<>(annotItems);
127
128     populateThresholdComboBox(threshold);
129
130     if (oldcs instanceof AnnotationColourGradient)
131     {
132       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
133       String label = getAnnotationMenuLabel(acg.getAnnotation());
134       annotations.setSelectedItem(label);
135       switch (acg.getAboveThreshold())
136       {
137       case AnnotationColourGradient.NO_THRESHOLD:
138         getThreshold().setSelectedIndex(0);
139         break;
140       case AnnotationColourGradient.ABOVE_THRESHOLD:
141         getThreshold().setSelectedIndex(1);
142         break;
143       case AnnotationColourGradient.BELOW_THRESHOLD:
144         getThreshold().setSelectedIndex(2);
145         break;
146       default:
147         throw new Error(MessageManager.getString(
148                 "error.implementation_error_dont_know_about_threshold_setting"));
149       }
150       thresholdIsMin.setSelected(acg.isThresholdIsMinMax());
151       thresholdValue.setText("" + acg.getAnnotationThreshold());
152     }
153
154     jbInit();
155     adjusting = false;
156
157     updateView();
158     frame.invalidate();
159     frame.pack();
160   }
161
162   /**
163    * Saves the global and group colour schemes as they are when the dialog is
164    * opened, so they can be restored on Cancel. Note we also save the threshold
165    * value for each annotation, in case this gets modified.
166    */
167   void saveInitialState()
168   {
169     oldcs = av.getGlobalColourScheme();
170     if (av.getAlignment().getGroups() != null)
171     {
172       oldgroupColours = new HashMap<>();
173       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
174       {
175         if (sg.getColourScheme() != null)
176         {
177           oldgroupColours.put(sg, sg.getColourScheme());
178         }
179       }
180     }
181
182     /*
183      * save any existing annotation threshold settings; note we make a copy
184      * of each in case the current threshold value gets amended
185      */
186     oldThresholds = new HashMap<>();
187     AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
188     if (anns != null)
189     {
190       for (AlignmentAnnotation ann : anns)
191       {
192         GraphLine thresh = ann.getThreshold();
193         oldThresholds.put(ann,
194                 thresh == null ? null : new GraphLine(thresh));
195       }
196     }
197   }
198
199   @Override
200   protected void jbInit()
201   {
202     super.jbInit();
203
204     minColour.setFont(JvSwingUtils.getLabelFont());
205     minColour.setBorder(BorderFactory.createEtchedBorder());
206     minColour.setPreferredSize(new Dimension(40, 20));
207     minColour.setToolTipText(MessageManager.getString("label.min_colour"));
208     minColour.addMouseListener(new MouseAdapter()
209     {
210       @Override
211       public void mousePressed(MouseEvent e)
212       {
213         if (minColour.isEnabled())
214         {
215           minColour_actionPerformed();
216         }
217       }
218     });
219     maxColour.setFont(JvSwingUtils.getLabelFont());
220     maxColour.setBorder(BorderFactory.createEtchedBorder());
221     maxColour.setPreferredSize(new Dimension(40, 20));
222     maxColour.setToolTipText(MessageManager.getString("label.max_colour"));
223     maxColour.addMouseListener(new MouseAdapter()
224     {
225       @Override
226       public void mousePressed(MouseEvent e)
227       {
228         if (maxColour.isEnabled())
229         {
230           maxColour_actionPerformed();
231         }
232       }
233     });
234
235     defColours = new JButton();
236     defColours.setOpaque(false);
237     defColours.setText(MessageManager.getString("action.set_defaults"));
238     defColours.setToolTipText(MessageManager
239             .getString("label.reset_min_max_colours_to_defaults"));
240     defColours.addActionListener(new ActionListener()
241     {
242
243       @Override
244       public void actionPerformed(ActionEvent arg0)
245       {
246         resetColours_actionPerformed();
247       }
248     });
249
250     useOriginalColours.setFont(JvSwingUtils.getLabelFont());
251     useOriginalColours.setOpaque(false);
252     useOriginalColours.setText(
253             MessageManager.getString("label.use_original_colours"));
254     useOriginalColours.addActionListener(new ActionListener()
255     {
256       @Override
257       public void actionPerformed(ActionEvent e)
258       {
259         originalColours_actionPerformed();
260       }
261     });
262     thresholdIsMin.setBackground(Color.white);
263     thresholdIsMin.setFont(JvSwingUtils.getLabelFont());
264     thresholdIsMin
265             .setText(MessageManager.getString("label.threshold_minmax"));
266     thresholdIsMin.addActionListener(new ActionListener()
267     {
268       @Override
269       public void actionPerformed(ActionEvent actionEvent)
270       {
271         thresholdIsMin_actionPerformed();
272       }
273     });
274     seqAssociated.setBackground(Color.white);
275     seqAssociated.setFont(JvSwingUtils.getLabelFont());
276     seqAssociated
277             .setText(MessageManager.getString("label.per_sequence_only"));
278     seqAssociated.addActionListener(new ActionListener()
279     {
280
281       @Override
282       public void actionPerformed(ActionEvent arg0)
283       {
284         seqAssociated_actionPerformed(annotations);
285       }
286     });
287
288     this.setLayout(new BorderLayout());
289     JPanel jPanel1 = new JPanel();
290     JPanel jPanel2 = new JPanel();
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, "grow, wrap");
298     jPanel2.add(seqAssociated);
299     jPanel2.add(useOriginalColours);
300     JPanel colpanel = new JPanel(new FlowLayout());
301     colpanel.setBackground(Color.white);
302     colpanel.add(minColour);
303     colpanel.add(maxColour);
304     jPanel2.add(colpanel, "wrap");
305     jPanel2.add(getThreshold());
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     this.validate();
313   }
314
315   protected void resetColours_actionPerformed()
316   {
317     setDefaultMinMax();
318     updateView();
319   }
320
321   private void setDefaultMinMax()
322   {
323     minColour.setBackground(
324             Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN", Color.orange));
325     maxColour.setBackground(
326             Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red));
327   }
328
329   public void minColour_actionPerformed()
330   {
331     Color col = JColorChooser.showDialog(this,
332             MessageManager.getString("label.select_colour_minimum_value"),
333             minColour.getBackground());
334     if (col != null)
335     {
336       minColour.setBackground(col);
337     }
338     minColour.repaint();
339     updateView();
340   }
341
342   public void maxColour_actionPerformed()
343   {
344     Color col = JColorChooser.showDialog(this,
345             MessageManager.getString("label.select_colour_maximum_value"),
346             maxColour.getBackground());
347     if (col != null)
348     {
349       maxColour.setBackground(col);
350     }
351     maxColour.repaint();
352     updateView();
353   }
354
355   @Override
356   public void reset()
357   {
358     av.setGlobalColourScheme(oldcs);
359     if (av.getAlignment().getGroups() != null)
360     {
361
362       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
363       {
364         sg.setColourScheme(oldgroupColours.get(sg));
365       }
366     }
367     for (Entry<AlignmentAnnotation, GraphLine> entry : oldThresholds
368             .entrySet())
369     {
370       entry.getKey().setThreshold(entry.getValue());
371     }
372   }
373
374   @Override
375   public void valueChanged(boolean updateAllAnnotation)
376   {
377     if (slider.isEnabled())
378     {
379       if (useOriginalColours.isSelected() && !(av
380               .getGlobalColourScheme() instanceof AnnotationColourGradient))
381       {
382         updateView();
383       }
384       getCurrentAnnotation().threshold.value = slider.getValue() / 1000f;
385       propagateSeqAssociatedThreshold(updateAllAnnotation,
386               getCurrentAnnotation());
387       ap.paintAlignment(false, false);
388     }
389   }
390
391   public void originalColours_actionPerformed()
392   {
393     boolean selected = useOriginalColours.isSelected();
394     if (selected)
395     {
396       reset();
397     }
398     maxColour.setEnabled(!selected);
399     minColour.setEnabled(!selected);
400     thresholdIsMin.setEnabled(!selected);
401     updateView();
402   }
403
404   @Override
405   public void updateView()
406   {
407     // Check if combobox is still adjusting
408     if (adjusting)
409     {
410       return;
411     }
412
413     setCurrentAnnotation(
414             av.getAlignment().getAlignmentAnnotation()[annmap[annotations
415                     .getSelectedIndex()]]);
416
417     int selectedThresholdItem = getSelectedThresholdItem(
418             getThreshold().getSelectedIndex());
419
420     slider.setEnabled(true);
421     thresholdValue.setEnabled(true);
422     thresholdIsMin.setEnabled(!useOriginalColours.isSelected());
423
424     if (selectedThresholdItem == AnnotationColourGradient.NO_THRESHOLD)
425     {
426       slider.setEnabled(false);
427       thresholdValue.setEnabled(false);
428       thresholdValue.setText("");
429       thresholdIsMin.setEnabled(false);
430     }
431     else if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD
432             && getCurrentAnnotation().threshold == null)
433     {
434       getCurrentAnnotation().setThreshold(new GraphLine(
435               (getCurrentAnnotation().graphMax
436                       - getCurrentAnnotation().graphMin) / 2f,
437               "Threshold", Color.black));
438     }
439
440     if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD)
441     {
442       adjusting = true;
443       float range = getCurrentAnnotation().graphMax * ONETHOUSAND
444               - getCurrentAnnotation().graphMin * ONETHOUSAND;
445
446       slider.setMinimum(
447               (int) (getCurrentAnnotation().graphMin * ONETHOUSAND));
448       slider.setMaximum(
449               (int) (getCurrentAnnotation().graphMax * ONETHOUSAND));
450       slider.setValue(
451               (int) (getCurrentAnnotation().threshold.value * ONETHOUSAND));
452       thresholdValue.setText(getCurrentAnnotation().threshold.value + "");
453       slider.setMajorTickSpacing((int) (range / 10f));
454       slider.setEnabled(true);
455       thresholdValue.setEnabled(true);
456       adjusting = false;
457     }
458     colorAlignmentContaining(getCurrentAnnotation(), selectedThresholdItem);
459
460     ap.alignmentChanged();
461   }
462
463   protected void colorAlignmentContaining(AlignmentAnnotation currentAnn,
464           int selectedThresholdOption)
465   {
466
467     AnnotationColourGradient acg = null;
468     if (useOriginalColours.isSelected())
469     {
470       acg = new AnnotationColourGradient(currentAnn,
471               av.getGlobalColourScheme(), selectedThresholdOption);
472     }
473     else
474     {
475       acg = new AnnotationColourGradient(currentAnn,
476               minColour.getBackground(), maxColour.getBackground(),
477               selectedThresholdOption);
478     }
479     acg.setSeqAssociated(seqAssociated.isSelected());
480
481     if (currentAnn.graphMin == 0f && currentAnn.graphMax == 0f)
482     {
483       acg.setPredefinedColours(true);
484     }
485
486     acg.setThresholdIsMinMax(thresholdIsMin.isSelected());
487
488     av.setGlobalColourScheme(acg);
489
490     if (av.getAlignment().getGroups() != null)
491     {
492
493       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
494       {
495         if (sg.cs == null)
496         {
497           continue;
498         }
499         sg.setColourScheme(
500                 acg.getInstance(sg, ap.av.getHiddenRepSequences()));
501       }
502     }
503   }
504
505   @Override
506   protected void sliderDragReleased()
507   {
508     super.sliderDragReleased();
509     ap.paintAlignment(true, true);
510   }
511
512 }