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