/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.gui;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.GraphLine;
import jalview.schemes.AnnotationColourGradient;
import jalview.util.MessageManager;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
@SuppressWarnings("serial")
public abstract class AnnotationRowFilter extends JPanel
{
protected AlignViewport av;
protected AlignmentPanel ap;
protected int[] annmap;
protected boolean adjusting = false;
protected JCheckBox seqAssociated = new JCheckBox();
protected JCheckBox percentThreshold = new JCheckBox();
protected JSlider slider = new JSlider();
protected JTextField thresholdValue = new JTextField(20);
protected JInternalFrame frame;
protected JButton ok = new JButton();
protected JButton cancel = new JButton();
/**
* enabled if the user is dragging the slider - try to keep updates to a
* minimun
*/
protected boolean sliderDragging = false;
protected JComboBox threshold = new JComboBox<>();
protected JComboBox annotations;
/*
* map from annotation to its menu item display label
* - so we know which item to pre-select on restore
*/
private Map annotationLabels;
private AlignmentAnnotation currentAnnotation;
/**
* Constructor
*
* @param viewport
* @param alignPanel
*/
public AnnotationRowFilter(AlignViewport viewport,
final AlignmentPanel alignPanel)
{
this.av = viewport;
this.ap = alignPanel;
thresholdValue.addFocusListener(new FocusAdapter()
{
@Override
public void focusLost(FocusEvent e)
{
thresholdValue_actionPerformed();
}
});
}
protected void addSliderChangeListener()
{
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent evt)
{
if (!adjusting)
{
setThresholdValueText();
valueChanged(!sliderDragging);
}
}
});
}
/**
* update the text field from the threshold slider. preserves state of
* 'adjusting' so safe to call in init.
*/
protected void setThresholdValueText()
{
boolean oldadj = adjusting;
adjusting = true;
if (percentThreshold.isSelected())
{
thresholdValue.setText("" + (slider.getValue() - slider.getMinimum())
* 100f / (slider.getMaximum() - slider.getMinimum()));
}
else
{
thresholdValue.setText((slider.getValue() / 1000f) + "");
}
adjusting = oldadj;
}
protected void addSliderMouseListeners()
{
slider.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent e)
{
sliderDragging = true;
super.mousePressed(e);
}
@Override
public void mouseDragged(MouseEvent e)
{
sliderDragging = true;
super.mouseDragged(e);
}
@Override
public void mouseReleased(MouseEvent evt)
{
if (sliderDragging)
{
sliderDragging = false;
valueChanged(true);
}
}
});
}
/**
* Builds and returns a list of menu items (display text) for choice of
* annotation. Also builds maps between annotations, their positions in the
* list, and their display labels in the list.
*
* @param isSeqAssociated
* @return
*/
public Vector getAnnotationItems(boolean isSeqAssociated)
{
annotationLabels = new HashMap<>();
Vector list = new Vector<>();
int index = 1;
int[] anmap = new int[av.getAlignment()
.getAlignmentAnnotation().length];
seqAssociated.setEnabled(false);
for (int i = 0; i < av.getAlignment()
.getAlignmentAnnotation().length; i++)
{
AlignmentAnnotation annotation = av.getAlignment()
.getAlignmentAnnotation()[i];
if (annotation.sequenceRef == null)
{
if (isSeqAssociated)
{
continue;
}
}
else
{
seqAssociated.setEnabled(true);
}
String label = annotation.label;
// add associated sequence ID if available
if (!isSeqAssociated && annotation.sequenceRef != null)
{
label = label + "_" + annotation.sequenceRef.getName();
}
// make label unique
if (!list.contains(label))
{
anmap[list.size()] = i;
list.add(label);
annotationLabels.put(annotation, label);
}
else
{
if (!isSeqAssociated)
{
anmap[list.size()] = i;
label = label + "_" + (index++);
list.add(label);
annotationLabels.put(annotation, label);
}
}
}
this.annmap = new int[list.size()];
System.arraycopy(anmap, 0, this.annmap, 0, this.annmap.length);
return list;
}
protected int getSelectedThresholdItem(int indexValue)
{
int selectedThresholdItem = -1;
if (indexValue == 1)
{
selectedThresholdItem = AnnotationColourGradient.ABOVE_THRESHOLD;
}
else if (indexValue == 2)
{
selectedThresholdItem = AnnotationColourGradient.BELOW_THRESHOLD;
}
return selectedThresholdItem;
}
public void ok_actionPerformed()
{
try
{
frame.setClosed(true);
} catch (Exception ex)
{
}
}
public void cancel_actionPerformed()
{
reset();
ap.paintAlignment(true, true);
try
{
frame.setClosed(true);
} catch (Exception ex)
{
}
}
protected void thresholdCheck_actionPerformed()
{
updateView();
}
protected void selectedAnnotationChanged()
{
updateView();
}
protected void threshold_actionPerformed()
{
updateView();
}
protected void thresholdValue_actionPerformed()
{
try
{
float f = Float.parseFloat(thresholdValue.getText());
if (percentThreshold.isSelected())
{
slider.setValue(slider.getMinimum() + ((int) ((f / 100f)
* (slider.getMaximum() - slider.getMinimum()))));
}
else
{
slider.setValue((int) (f * 1000));
}
updateView();
} catch (NumberFormatException ex)
{
}
}
protected void percentageValue_actionPerformed()
{
setThresholdValueText();
}
protected void thresholdIsMin_actionPerformed()
{
updateView();
}
protected void populateThresholdComboBox(JComboBox thresh)
{
thresh.addItem(MessageManager
.getString("label.threshold_feature_no_threshold"));
thresh.addItem(MessageManager
.getString("label.threshold_feature_above_threshold"));
thresh.addItem(MessageManager
.getString("label.threshold_feature_below_threshold"));
}
/**
* Rebuilds the drop-down list of annotations to choose from when the 'per
* sequence only' checkbox is checked or unchecked.
*
* @param anns
*/
protected void seqAssociated_actionPerformed(JComboBox anns)
{
adjusting = true;
String cursel = (String) anns.getSelectedItem();
boolean isvalid = false;
boolean isseqs = seqAssociated.isSelected();
anns.removeAllItems();
for (String anitem : getAnnotationItems(seqAssociated.isSelected()))
{
if (anitem.equals(cursel) || (isseqs && cursel.startsWith(anitem)))
{
isvalid = true;
cursel = anitem;
}
anns.addItem(anitem);
}
if (isvalid)
{
anns.setSelectedItem(cursel);
}
else
{
if (anns.getItemCount() > 0)
{
anns.setSelectedIndex(0);
}
}
adjusting = false;
updateView();
}
protected void propagateSeqAssociatedThreshold(boolean allAnnotation,
AlignmentAnnotation annotation)
{
if (annotation.sequenceRef == null || annotation.threshold == null)
{
return;
}
float thr = annotation.threshold.value;
for (int i = 0; i < av.getAlignment()
.getAlignmentAnnotation().length; i++)
{
AlignmentAnnotation aa = av.getAlignment()
.getAlignmentAnnotation()[i];
if (aa.label.equals(annotation.label)
&& (annotation.getCalcId() == null ? aa.getCalcId() == null
: annotation.getCalcId().equals(aa.getCalcId())))
{
if (aa.threshold == null)
{
aa.threshold = new GraphLine(annotation.threshold);
}
else
{
aa.threshold.value = thr;
}
}
}
}
public AlignmentAnnotation getCurrentAnnotation()
{
return currentAnnotation;
}
protected void setCurrentAnnotation(AlignmentAnnotation annotation)
{
this.currentAnnotation = annotation;
}
/**
* update associated view model and trigger any necessary repaints.
*
* @param updateAllAnnotation
*/
protected abstract void valueChanged(boolean updateAllAnnotation);
protected abstract void updateView();
protected abstract void reset();
protected String getAnnotationMenuLabel(AlignmentAnnotation ann)
{
return annotationLabels.get(ann);
}
protected void jbInit()
{
ok.setOpaque(false);
ok.setText(MessageManager.getString("action.ok"));
ok.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
ok_actionPerformed();
}
});
cancel.setOpaque(false);
cancel.setText(MessageManager.getString("action.cancel"));
cancel.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
cancel_actionPerformed();
}
});
annotations.addItemListener(new ItemListener()
{
@Override
public void itemStateChanged(ItemEvent e)
{
selectedAnnotationChanged();
}
});
annotations.setToolTipText(
MessageManager.getString("info.select_annotation_row"));
threshold.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
threshold_actionPerformed();
}
});
thresholdValue.setEnabled(false);
thresholdValue.setColumns(7);
thresholdValue.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
thresholdValue_actionPerformed();
}
});
percentThreshold
.setText(MessageManager.getString("label.as_percentage"));
percentThreshold.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
if (!adjusting)
{
percentageValue_actionPerformed();
}
}
});
slider.setPaintLabels(false);
slider.setPaintTicks(true);
slider.setBackground(Color.white);
slider.setEnabled(false);
slider.setOpaque(false);
slider.setPreferredSize(new Dimension(100, 32));
}
public JComboBox getThreshold()
{
return threshold;
}
public void setThreshold(JComboBox thresh)
{
this.threshold = thresh;
}
public JComboBox getAnnotations()
{
return annotations;
}
public void setAnnotations(JComboBox anns)
{
this.annotations = anns;
}
}