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