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