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