JAL-2346 restore choice of annotation for colouring faithfully
[jalview.git] / src / jalview / gui / AnnotationRowFilter.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 java.awt.event.MouseAdapter;
24 import java.awt.event.MouseEvent;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Vector;
28
29 import jalview.datamodel.AlignmentAnnotation;
30 import jalview.datamodel.GraphLine;
31 import jalview.datamodel.SequenceGroup;
32 import jalview.schemes.AnnotationColourGradient;
33 import jalview.util.MessageManager;
34
35 import javax.swing.JButton;
36 import javax.swing.JCheckBox;
37 import javax.swing.JComboBox;
38 import javax.swing.JInternalFrame;
39 import javax.swing.JPanel;
40 import javax.swing.JSlider;
41 import javax.swing.JTextField;
42 import javax.swing.event.ChangeEvent;
43 import javax.swing.event.ChangeListener;
44
45 @SuppressWarnings("serial")
46 public abstract class AnnotationRowFilter extends JPanel
47 {
48   protected AlignViewport av;
49
50   protected AlignmentPanel ap;
51
52   protected int[] annmap;
53
54   /*
55    * map from annotation to its menu item display label
56    * - so we know which item to pre-select on restore
57    */
58   private Map<AlignmentAnnotation, String> annotationLabels;
59
60   protected boolean enableSeqAss = false;
61
62   private AlignmentAnnotation currentAnnotation;
63
64   protected boolean adjusting = false;
65
66   protected JCheckBox currentColours = new JCheckBox();
67
68   protected JPanel minColour = new JPanel();
69
70   protected JPanel maxColour = new JPanel();
71
72   protected JCheckBox seqAssociated = new JCheckBox();
73
74   protected JCheckBox thresholdIsMin = new JCheckBox();
75
76   protected JSlider slider = new JSlider();
77
78   protected JTextField thresholdValue = new JTextField(20);
79
80   protected JInternalFrame frame;
81
82   protected JButton ok = new JButton();
83
84   protected JButton cancel = new JButton();
85
86   /**
87    * enabled if the user is dragging the slider - try to keep updates to a
88    * minimun
89    */
90   protected boolean sliderDragging = false;
91
92   protected void addSliderChangeListener()
93   {
94
95     slider.addChangeListener(new ChangeListener()
96     {
97       @Override
98       public void stateChanged(ChangeEvent evt)
99       {
100         if (!adjusting)
101         {
102           thresholdValue.setText((slider.getValue() / 1000f) + "");
103           valueChanged(!sliderDragging);
104         }
105       }
106     });
107   }
108
109   protected void addSliderMouseListeners()
110   {
111
112     slider.addMouseListener(new MouseAdapter()
113     {
114       @Override
115       public void mousePressed(MouseEvent e)
116       {
117         sliderDragging = true;
118         super.mousePressed(e);
119       }
120
121       @Override
122       public void mouseDragged(MouseEvent e)
123       {
124         sliderDragging = true;
125         super.mouseDragged(e);
126       }
127
128       @Override
129       public void mouseReleased(MouseEvent evt)
130       {
131         if (sliderDragging)
132         {
133           sliderDragging = false;
134           valueChanged(true);
135         }
136         ap.paintAlignment(true);
137       }
138     });
139   }
140
141   public AnnotationRowFilter(AlignViewport av, final AlignmentPanel ap)
142   {
143     this.av = av;
144     this.ap = ap;
145   }
146
147   public AnnotationRowFilter()
148   {
149
150   }
151
152   /**
153    * Builds and returns a list of menu items (display text) for choice of
154    * annotation. Also builds maps between annotations, their positions in the
155    * list, and their display labels in the list.
156    * 
157    * @param isSeqAssociated
158    * @return
159    */
160   public Vector<String> getAnnotationItems(boolean isSeqAssociated)
161   {
162     annotationLabels = new HashMap<AlignmentAnnotation, String>();
163
164     Vector<String> list = new Vector<String>();
165     int index = 1;
166     int[] anmap = new int[av.getAlignment().getAlignmentAnnotation().length];
167     for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
168     {
169       AlignmentAnnotation annotation = av.getAlignment()
170               .getAlignmentAnnotation()[i];
171       if (annotation.sequenceRef == null)
172       {
173         if (isSeqAssociated)
174         {
175           continue;
176         }
177       }
178       else
179       {
180         enableSeqAss = true;
181       }
182       String label = annotation.label;
183       // add associated sequence ID if available
184       if (!isSeqAssociated && annotation.sequenceRef != null)
185       {
186         label = label + "_" + annotation.sequenceRef.getName();
187       }
188       // make label unique
189       if (!list.contains(label))
190       {
191         anmap[list.size()] = i;
192         list.add(label);
193         annotationLabels.put(annotation, label);
194       }
195       else
196       {
197         if (!isSeqAssociated)
198         {
199           anmap[list.size()] = i;
200           label = label + "_" + (index++);
201           list.add(label);
202           annotationLabels.put(annotation, label);
203         }
204       }
205     }
206     this.annmap = new int[list.size()];
207     System.arraycopy(anmap, 0, this.annmap, 0, this.annmap.length);
208     return list;
209   }
210
211   protected int getSelectedThresholdItem(int indexValue)
212   {
213     int selectedThresholdItem = -1;
214     if (indexValue == 1)
215     {
216       selectedThresholdItem = AnnotationColourGradient.ABOVE_THRESHOLD;
217     }
218     else if (indexValue == 2)
219     {
220       selectedThresholdItem = AnnotationColourGradient.BELOW_THRESHOLD;
221     }
222     return selectedThresholdItem;
223   }
224
225   public void modelChanged()
226   {
227     seqAssociated.setEnabled(enableSeqAss);
228   }
229
230   public void ok_actionPerformed()
231   {
232     try
233     {
234       frame.setClosed(true);
235     } catch (Exception ex)
236     {
237     }
238   }
239
240   public void cancel_actionPerformed()
241   {
242     reset();
243     ap.paintAlignment(true);
244     try
245     {
246       frame.setClosed(true);
247     } catch (Exception ex)
248     {
249     }
250   }
251
252   public void thresholdCheck_actionPerformed()
253   {
254     updateView();
255   }
256
257   public void annotations_actionPerformed()
258   {
259     updateView();
260   }
261
262   public void threshold_actionPerformed()
263   {
264     updateView();
265   }
266
267   public void thresholdValue_actionPerformed()
268   {
269     try
270     {
271       float f = Float.parseFloat(thresholdValue.getText());
272       slider.setValue((int) (f * 1000));
273       updateView();
274     } catch (NumberFormatException ex)
275     {
276     }
277   }
278
279   public void thresholdIsMin_actionPerformed()
280   {
281     updateView();
282   }
283
284   protected void populateThresholdComboBox(JComboBox<String> threshold)
285   {
286     threshold.addItem(MessageManager
287             .getString("label.threshold_feature_no_threshold"));
288     threshold.addItem(MessageManager
289             .getString("label.threshold_feature_above_threshold"));
290     threshold.addItem(MessageManager
291             .getString("label.threshold_feature_below_threshold"));
292   }
293
294   /**
295    * Rebuilds the drop-down list of annotations to choose from when the 'per
296    * sequence only' checkbox is checked or unchecked.
297    * 
298    * @param annotations
299    */
300   protected void seqAssociated_actionPerformed(JComboBox<String> annotations)
301   {
302     adjusting = true;
303     String cursel = (String) annotations.getSelectedItem();
304     boolean isvalid = false, isseqs = seqAssociated.isSelected();
305     annotations.removeAllItems();
306     for (String anitem : getAnnotationItems(seqAssociated.isSelected()))
307     {
308       if (anitem.equals(cursel) || (isseqs && cursel.startsWith(anitem)))
309       {
310         isvalid = true;
311         cursel = anitem;
312       }
313       annotations.addItem(anitem);
314     }
315     adjusting = false;
316     if (isvalid)
317     {
318       annotations.setSelectedItem(cursel);
319     }
320     else
321     {
322       if (annotations.getItemCount() > 0)
323       {
324         annotations.setSelectedIndex(0);
325       }
326     }
327   }
328
329   protected void propagateSeqAssociatedThreshold(boolean allAnnotation,
330           AlignmentAnnotation annotation)
331   {
332     if (annotation.sequenceRef == null || annotation.threshold == null)
333     {
334       return;
335     }
336
337     float thr = annotation.threshold.value;
338     for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
339     {
340       AlignmentAnnotation aa = av.getAlignment().getAlignmentAnnotation()[i];
341       if (aa.label.equals(annotation.label)
342               && (annotation.getCalcId() == null ? aa.getCalcId() == null
343                       : annotation.getCalcId().equals(aa.getCalcId())))
344       {
345         if (aa.threshold == null)
346         {
347           aa.threshold = new GraphLine(annotation.threshold);
348         }
349         else
350         {
351           aa.threshold.value = thr;
352         }
353       }
354     }
355   }
356
357   protected boolean colorAlignmentContaining(
358           AlignmentAnnotation currentAnn, int selectedThresholdOption)
359   {
360
361     AnnotationColourGradient acg = null;
362     if (currentColours.isSelected())
363     {
364       acg = new AnnotationColourGradient(currentAnn,
365               av.getGlobalColourScheme(), selectedThresholdOption);
366     }
367     else
368     {
369       acg = new AnnotationColourGradient(currentAnn,
370               minColour.getBackground(), maxColour.getBackground(),
371               selectedThresholdOption);
372     }
373     acg.setSeqAssociated(seqAssociated.isSelected());
374
375     if (currentAnn.graphMin == 0f && currentAnn.graphMax == 0f)
376     {
377       acg.setPredefinedColours(true);
378     }
379
380     acg.setThresholdIsMinMax(thresholdIsMin.isSelected());
381
382     av.setGlobalColourScheme(acg);
383
384     if (av.getAlignment().getGroups() != null)
385     {
386
387       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
388       {
389         if (sg.cs == null)
390         {
391           continue;
392         }
393
394         if (currentColours.isSelected())
395         {
396           sg.cs = new AnnotationColourGradient(currentAnn, sg.cs,
397                   selectedThresholdOption);
398           ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
399                   .isSelected());
400         }
401         else
402         {
403           sg.cs = new AnnotationColourGradient(currentAnn,
404                   minColour.getBackground(), maxColour.getBackground(),
405                   selectedThresholdOption);
406           ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
407                   .isSelected());
408         }
409       }
410     }
411     return false;
412   }
413
414   public AlignmentAnnotation getCurrentAnnotation()
415   {
416     return currentAnnotation;
417   }
418
419   public void setCurrentAnnotation(AlignmentAnnotation currentAnnotation)
420   {
421     this.currentAnnotation = currentAnnotation;
422   }
423
424   public abstract void valueChanged(boolean updateAllAnnotation);
425
426   public abstract void updateView();
427
428   public abstract void reset();
429
430   protected String getAnnotationMenuLabel(AlignmentAnnotation ann)
431   {
432     return annotationLabels.get(ann);
433   }
434 }