2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.GraphLine;
25 import jalview.schemes.AnnotationColourGradient;
26 import jalview.util.MessageManager;
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.math.BigDecimal;
39 import java.math.MathContext;
40 import java.util.HashMap;
42 import java.util.Vector;
44 import javax.swing.JButton;
45 import javax.swing.JCheckBox;
46 import javax.swing.JComboBox;
47 import javax.swing.JInternalFrame;
48 import javax.swing.JPanel;
49 import javax.swing.JTextField;
50 import javax.swing.event.ChangeEvent;
51 import javax.swing.event.ChangeListener;
53 @SuppressWarnings("serial")
54 public abstract class AnnotationRowFilter extends JPanel
56 private static final String TWO_DP = "%.2f";
58 private final static MathContext FOUR_SIG_FIG = new MathContext(4);
60 protected AlignViewport av;
62 protected AlignmentPanel ap;
64 protected int[] annmap;
66 protected boolean adjusting = false;
68 protected JCheckBox seqAssociated = new JCheckBox();
70 protected JCheckBox percentThreshold = new JCheckBox();
72 protected Slider slider;
74 protected JTextField thresholdValue = new JTextField(20);
76 protected JInternalFrame frame;
78 protected JButton ok = new JButton();
80 protected JButton cancel = new JButton();
83 * enabled if the user is dragging the slider - try to keep updates to a
86 protected boolean sliderDragging = false;
88 protected JComboBox<String> threshold = new JComboBox<>();
90 protected JComboBox<String> annotations;
93 * map from annotation to its menu item display label
94 * - so we know which item to pre-select on restore
96 private Map<AlignmentAnnotation, String> annotationLabels;
98 private AlignmentAnnotation currentAnnotation;
106 public AnnotationRowFilter(AlignViewport viewport,
107 final AlignmentPanel alignPanel)
110 this.ap = alignPanel;
111 this.slider = new Slider(0f, 100f, 50f);
113 thresholdValue.addFocusListener(new FocusAdapter()
116 public void focusLost(FocusEvent e)
118 thresholdValue_actionPerformed();
123 protected void addSliderChangeListener()
126 slider.addChangeListener(new ChangeListener()
129 public void stateChanged(ChangeEvent evt)
133 setThresholdValueText();
134 valueChanged(!sliderDragging);
141 * update the text field from the threshold slider. preserves state of
142 * 'adjusting' so safe to call in init.
144 protected void setThresholdValueText()
146 boolean oldadj = adjusting;
148 if (percentThreshold.isSelected())
151 .setText(String.format(TWO_DP, getSliderPercentageValue()));
156 * round to 4 significant digits without trailing zeroes
158 float f = getSliderValue();
159 BigDecimal formatted = new BigDecimal(f).round(FOUR_SIG_FIG)
160 .stripTrailingZeros();
161 thresholdValue.setText(formatted.toPlainString());
167 * Answers the value of the slider position (descaled to 'true' value)
171 protected float getSliderValue()
173 return slider.getSliderValue();
177 * Sets the slider value (scaled from the true value to the slider range)
181 protected void setSliderValue(float value)
183 slider.setSliderValue(value);
186 * Answers the value of the slider position as a percentage between minimum and
187 * maximum of its range
191 protected float getSliderPercentageValue()
193 return slider.getSliderPercentageValue();
197 * Sets the slider position for a given percentage value of its min-max range
201 protected void setSliderPercentageValue(float pct)
203 slider.setSliderPercentageValue(pct);
206 protected void addSliderMouseListeners()
209 slider.addMouseListener(new MouseAdapter()
212 public void mousePressed(MouseEvent e)
214 sliderDragging = true;
215 super.mousePressed(e);
219 public void mouseDragged(MouseEvent e)
221 sliderDragging = true;
222 super.mouseDragged(e);
226 public void mouseReleased(MouseEvent evt)
228 sliderDragReleased();
234 * Builds and returns a list of menu items (display text) for choice of
235 * annotation. Also builds maps between annotations, their positions in the
236 * list, and their display labels in the list.
238 * @param isSeqAssociated
241 public Vector<String> getAnnotationItems(boolean isSeqAssociated)
243 annotationLabels = new HashMap<>();
245 Vector<String> list = new Vector<>();
247 int[] anmap = new int[av.getAlignment()
248 .getAlignmentAnnotation().length];
249 seqAssociated.setEnabled(false);
250 for (int i = 0; i < av.getAlignment()
251 .getAlignmentAnnotation().length; i++)
253 AlignmentAnnotation annotation = av.getAlignment()
254 .getAlignmentAnnotation()[i];
255 if (annotation.sequenceRef == null)
264 seqAssociated.setEnabled(true);
266 String label = annotation.label;
267 // add associated sequence ID if available
268 if (!isSeqAssociated && annotation.sequenceRef != null)
270 label = label + "_" + annotation.sequenceRef.getName();
273 if (!list.contains(label))
275 anmap[list.size()] = i;
277 annotationLabels.put(annotation, label);
281 if (!isSeqAssociated)
283 anmap[list.size()] = i;
284 label = label + "_" + (index++);
286 annotationLabels.put(annotation, label);
290 this.annmap = new int[list.size()];
291 System.arraycopy(anmap, 0, this.annmap, 0, this.annmap.length);
295 protected int getSelectedThresholdItem(int indexValue)
297 int selectedThresholdItem = -1;
300 selectedThresholdItem = AnnotationColourGradient.ABOVE_THRESHOLD;
302 else if (indexValue == 2)
304 selectedThresholdItem = AnnotationColourGradient.BELOW_THRESHOLD;
306 return selectedThresholdItem;
309 public void ok_actionPerformed()
313 frame.setClosed(true);
314 } catch (Exception ex)
319 public void cancel_actionPerformed()
322 ap.paintAlignment(true, true);
325 frame.setClosed(true);
326 } catch (Exception ex)
331 protected void thresholdCheck_actionPerformed()
336 protected void selectedAnnotationChanged()
341 protected void threshold_actionPerformed()
347 * Updates the slider position, and the display, for an update in the slider's
350 protected void thresholdValue_actionPerformed()
354 float f = Float.parseFloat(thresholdValue.getText());
355 if (percentThreshold.isSelected())
357 setSliderPercentageValue(f);
364 } catch (NumberFormatException ex)
369 protected void percentageValue_actionPerformed()
371 setThresholdValueText();
374 protected void thresholdIsMin_actionPerformed()
379 protected void populateThresholdComboBox(JComboBox<String> thresh)
381 thresh.addItem(MessageManager
382 .getString("label.threshold_feature_no_threshold"));
383 thresh.addItem(MessageManager
384 .getString("label.threshold_feature_above_threshold"));
385 thresh.addItem(MessageManager
386 .getString("label.threshold_feature_below_threshold"));
390 * Rebuilds the drop-down list of annotations to choose from when the 'per
391 * sequence only' checkbox is checked or unchecked.
395 protected void seqAssociated_actionPerformed(JComboBox<String> anns)
398 String cursel = (String) anns.getSelectedItem();
399 boolean isvalid = false;
400 boolean isseqs = seqAssociated.isSelected();
401 anns.removeAllItems();
402 for (String anitem : getAnnotationItems(seqAssociated.isSelected()))
404 if (anitem.equals(cursel) || (isseqs && cursel.startsWith(anitem)))
409 anns.addItem(anitem);
413 anns.setSelectedItem(cursel);
417 if (anns.getItemCount() > 0)
419 anns.setSelectedIndex(0);
427 protected void propagateSeqAssociatedThreshold(boolean allAnnotation,
428 AlignmentAnnotation annotation)
430 if (annotation.sequenceRef == null || annotation.threshold == null)
435 float thr = annotation.threshold.value;
436 for (int i = 0; i < av.getAlignment()
437 .getAlignmentAnnotation().length; i++)
439 AlignmentAnnotation aa = av.getAlignment()
440 .getAlignmentAnnotation()[i];
441 if (aa.label.equals(annotation.label)
442 && (annotation.getCalcId() == null ? aa.getCalcId() == null
443 : annotation.getCalcId().equals(aa.getCalcId())))
445 if (aa.threshold == null)
447 aa.threshold = new GraphLine(annotation.threshold);
451 aa.threshold.value = thr;
457 public AlignmentAnnotation getCurrentAnnotation()
459 return currentAnnotation;
462 protected void setCurrentAnnotation(AlignmentAnnotation annotation)
464 this.currentAnnotation = annotation;
468 * update associated view model and trigger any necessary repaints.
470 * @param updateAllAnnotation
472 protected abstract void valueChanged(boolean updateAllAnnotation);
474 protected abstract void updateView();
476 protected abstract void reset();
478 protected String getAnnotationMenuLabel(AlignmentAnnotation ann)
480 return annotationLabels.get(ann);
483 protected void jbInit()
486 ok.setText(MessageManager.getString("action.ok"));
487 ok.addActionListener(new ActionListener()
490 public void actionPerformed(ActionEvent e)
492 ok_actionPerformed();
496 cancel.setOpaque(false);
497 cancel.setText(MessageManager.getString("action.cancel"));
498 cancel.addActionListener(new ActionListener()
501 public void actionPerformed(ActionEvent e)
503 cancel_actionPerformed();
507 annotations.addItemListener(new ItemListener()
510 public void itemStateChanged(ItemEvent e)
512 selectedAnnotationChanged();
515 annotations.setToolTipText(
516 MessageManager.getString("info.select_annotation_row"));
518 threshold.addActionListener(new ActionListener()
521 public void actionPerformed(ActionEvent e)
523 threshold_actionPerformed();
527 thresholdValue.setEnabled(false);
528 thresholdValue.setColumns(7);
529 thresholdValue.addActionListener(new ActionListener()
532 public void actionPerformed(ActionEvent e)
534 thresholdValue_actionPerformed();
539 .setText(MessageManager.getString("label.as_percentage"));
540 percentThreshold.addActionListener(new ActionListener()
543 public void actionPerformed(ActionEvent e)
547 percentageValue_actionPerformed();
551 slider.setPaintLabels(false);
552 slider.setPaintTicks(true);
553 slider.setBackground(Color.white);
554 slider.setEnabled(false);
555 slider.setOpaque(false);
556 slider.setPreferredSize(new Dimension(100, 32));
559 public JComboBox<String> getThreshold()
564 public void setThreshold(JComboBox<String> thresh)
566 this.threshold = thresh;
569 public JComboBox<String> getAnnotations()
574 public void setAnnotations(JComboBox<String> anns)
576 this.annotations = anns;
579 protected void sliderDragReleased()
583 sliderDragging = false;
589 * Sets the min-max range and current value of the slider, with rescaling from
590 * true values to slider range as required
596 protected void setSliderModel(float min, float max, float value)
598 slider.setSliderModel(min, max, value);
601 * tick mark every 10th position
603 slider.setMajorTickSpacing(
604 (slider.getMaximum() - slider.getMinimum()) / 10);