import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
+import java.util.HashMap;
import java.util.Hashtable;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
menuItem = new JMenuItem();
menuItem.setText(pdb.getId());
- menuItem.addActionListener(new java.awt.event.ActionListener()
+ menuItem.addActionListener(new ActionListener()
{
- public void actionPerformed(ActionEvent e)
- {
- // TODO re JAL-860: optionally open dialog or provide a menu entry
- // allowing user to open just one structure per sequence
- // new AppJmol(pdb, ap.av.collateForPDB(new PDBEntry[]
- // { pdb })[0], null, ap);
- new StructureViewer(ap.getStructureSelectionManager())
- .viewStructures(pdb,
- ap.av.collateForPDB(new PDBEntry[]
- { pdb })[0], null, ap);
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // TODO re JAL-860: optionally open dialog or provide a menu entry
+ // allowing user to open just one structure per sequence
+ // new AppJmol(pdb, ap.av.collateForPDB(new PDBEntry[]
+ // { pdb })[0], null, ap);
+ new StructureViewer(ap.getStructureSelectionManager())
+ .viewStructures(pdb,
+ ap.av.collateForPDB(new PDBEntry[]
+ { pdb })[0], null, ap);
}
});
viewStructureMenu.add(menuItem);
"label.2d_rna_structure_line", new String[]
{ structureLine }));
menuItem.addActionListener(new java.awt.event.ActionListener()
-
{
@Override
public void actionPerformed(ActionEvent e)
*/
protected void buildAnnotationTypesMenus()
{
+ final SequenceGroup selectionGroup = ap.av.getSelectionGroup();
+ if (selectionGroup == null)
+ {
+ // this menu option is only for a selection
+ return;
+ }
+
showAnnotationsMenu.removeAll();
hideAnnotationsMenu.removeAll();
final List<String> all = Arrays.asList(ALL_ANNOTATIONS);
- addAnnotationTypeToShowHide(showAnnotationsMenu, all, true, true);
- addAnnotationTypeToShowHide(hideAnnotationsMenu, all, true, false);
+ addAnnotationTypeToShowHide(showAnnotationsMenu, "", all, true, true);
+ addAnnotationTypeToShowHide(hideAnnotationsMenu, "", all, true, false);
showAnnotationsMenu.addSeparator();
hideAnnotationsMenu.addSeparator();
BitSet visibleGraphGroups = PopupMenu
.getVisibleLineGraphGroups(annotations);
- List<List<String>> shownTypes = new ArrayList<List<String>>();
- List<List<String>> hiddenTypes = new ArrayList<List<String>>();
+ /*
+ * Find shown/hidden annotations types, distinguished by source (calcId),
+ * and grouped by graphGroup.
+ */
+ Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
+ Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
PopupMenu.getAnnotationTypesForShowHide(shownTypes, hiddenTypes,
- visibleGraphGroups, annotations, ap.av.getSelectionGroup());
+ visibleGraphGroups, annotations, selectionGroup);
- for (List<String> types : hiddenTypes)
+ for (String calcId : hiddenTypes.keySet())
{
- addAnnotationTypeToShowHide(showAnnotationsMenu, types, false, true);
+ for (List<String> type : hiddenTypes.get(calcId))
+ {
+ addAnnotationTypeToShowHide(showAnnotationsMenu, calcId, type,
+ false, true);
+ }
}
- for (List<String> types : shownTypes)
+ for (String calcId : shownTypes.keySet())
{
- addAnnotationTypeToShowHide(hideAnnotationsMenu, types, false, false);
+ for (List<String> type : shownTypes.get(calcId))
+ {
+ addAnnotationTypeToShowHide(hideAnnotationsMenu, calcId, type,
+ false, false);
+ }
}
}
* </ul>
*
* @param shownTypes
+ * a map, keyed by calcId (annotation source), whose entries are the
+ * lists of annotation types found for the calcId; each annotation
+ * type in turn may be a list (in the case of grouped annotations)
* @param hiddenTypes
+ * a map, similar to shownTypes, but for hidden annotation types
* @param visibleGraphGroups
+ * a lookup keyed by graphGroup identifier
* @param annotations
+ * the annotations on the alignment to scan
* @param sequenceGroup
+ * the sequence group to restrict search to
*/
public static void getAnnotationTypesForShowHide(
- List<List<String>> shownTypes, List<List<String>> hiddenTypes,
+ Map<String, List<List<String>>> shownTypes,
+ Map<String, List<List<String>>> hiddenTypes,
BitSet visibleGraphGroups, AlignmentAnnotation[] annotations,
SequenceGroup sequenceGroup)
{
- // lookup table, key = graph group, value = list of types in the group
- Map<Integer, List<String>> groupLabels = new LinkedHashMap<Integer, List<String>>();
+ /*
+ * Build a lookup, by calcId (annotation source), of all annotation types in
+ * each graph group.
+ */
+ Map<String, Map<Integer, List<String>>> groupLabels = new HashMap<String, Map<Integer, List<String>>>();
+ // trackers for which calcId!label combinations we have dealt with
List<String> addedToShown = new ArrayList<String>();
List<String> addedToHidden = new ArrayList<String>();
|| (aa.sequenceRef != null && sequenceGroup.getSequences()
.contains(aa.sequenceRef)))
{
+ String calcId = aa.getCalcId();
+
/*
* Build a 'composite label' for types in line graph groups.
*/
final List<String> labelAsList = new ArrayList<String>();
- labelAsList.add(aa.label);
+ final String displayLabel = aa.label;
+ labelAsList.add(displayLabel);
if (aa.graph == AlignmentAnnotation.LINE_GRAPH
&& aa.graphGroup > -1)
{
- if (groupLabels.containsKey(aa.graphGroup))
+ if (!groupLabels.containsKey(calcId))
+ {
+ groupLabels.put(calcId, new HashMap<Integer, List<String>>());
+ }
+ Map<Integer, List<String>> groupLabelsForCalcId = groupLabels
+ .get(calcId);
+ if (groupLabelsForCalcId.containsKey(aa.graphGroup))
{
- if (!groupLabels.get(aa.graphGroup).contains(aa.label))
+ if (!groupLabelsForCalcId.get(aa.graphGroup).contains(
+ displayLabel))
{
- groupLabels.get(aa.graphGroup).add(aa.label);
+ groupLabelsForCalcId.get(aa.graphGroup).add(displayLabel);
}
}
else
{
- groupLabels.put(aa.graphGroup, labelAsList);
+ groupLabelsForCalcId.put(aa.graphGroup, labelAsList);
}
}
- else if (aa.visible && !addedToShown.contains(aa.label))
- {
- shownTypes.add(labelAsList);
- addedToShown.add(aa.label);
- }
else
+ /*
+ * 'Simple case' - not a grouped annotation type - list of one label
+ * only
+ */
{
- if (!aa.visible && !addedToHidden.contains(aa.label))
+ String rememberAs = calcId + "!" + displayLabel;
+ if (aa.visible && !addedToShown.contains(rememberAs))
{
- hiddenTypes.add(labelAsList);
- addedToHidden.add(aa.label);
+ if (!shownTypes.containsKey(calcId))
+ {
+ shownTypes.put(calcId, new ArrayList<List<String>>());
+ }
+ shownTypes.get(calcId).add(labelAsList);
+ addedToShown.add(rememberAs);
+ }
+ else
+ {
+ if (!aa.visible && !addedToHidden.contains(rememberAs))
+ {
+ if (!hiddenTypes.containsKey(calcId))
+ {
+ hiddenTypes.put(calcId, new ArrayList<List<String>>());
+ }
+ hiddenTypes.get(calcId).add(labelAsList);
+ addedToHidden.add(rememberAs);
+ }
}
}
}
* finally add the 'composite group labels' to the appropriate lists,
* depending on whether the group is identified as visible or hidden
*/
- for (int group : groupLabels.keySet())
+ for (String calcId : groupLabels.keySet())
{
- final List<String> groupLabel = groupLabels.get(group);
- if (visibleGraphGroups.get(group))
+ for (int group : groupLabels.get(calcId).keySet())
{
- if (!shownTypes.contains(groupLabel))
+ final List<String> groupLabel = groupLabels.get(calcId).get(group);
+ if (visibleGraphGroups.get(group))
{
- shownTypes.add(groupLabel);
+ if (!shownTypes.containsKey(calcId))
+ {
+ shownTypes.put(calcId, new ArrayList<List<String>>());
+ }
+ shownTypes.get(calcId).add(groupLabel);
+ }
+ else
+ {
+ if (!hiddenTypes.containsKey(calcId))
+ {
+ hiddenTypes.put(calcId, new ArrayList<List<String>>());
+ }
+ hiddenTypes.get(calcId).add(groupLabel);
}
- }
- else if (!hiddenTypes.contains(groupLabel))
- {
- hiddenTypes.add(groupLabel);
}
}
}
*
* @param showOrHideMenu
* the menu to add to
+ * @param calcId
* @param types
* the label to add
* @param allTypes
* type, else hide
*/
protected void addAnnotationTypeToShowHide(JMenu showOrHideMenu,
- final Collection<String> types, final boolean allTypes,
+ String calcId, final List<String> types, final boolean allTypes,
final boolean actionIsShow)
{
String label = types.toString(); // [a, b, c]
label = label.substring(1, label.length() - 1);
final JMenuItem item = new JMenuItem(label);
+ item.setToolTipText(calcId);
item.addActionListener(new java.awt.event.ActionListener()
{
@Override
for (AlignmentAnnotation aa : ap.getAlignment()
.getAlignmentAnnotation())
{
+ // TODO: select by calcId (source of annotation) as well as label
+ // e.g. by refactoring of buildAnnotationTypeMenus to as
+ // to construct the actionPerformed methods as the calcId/labels are found
if (anyType || types.contains(aa.label))
{
if ((aa.sequenceRef != null)
.getString("label.show_annotations"));
hideAnnotationsMenu.setText(MessageManager
.getString("label.hide_annotations"));
- final List<AlignmentAnnotation> referenceAnns = getDatasequenceAnnotationsNotOnAlignment(ap.av
- .getSelectionGroup());
- addDatasequenceAnnotations.setText(MessageManager
- .getString("label.add_reference_annotations"));
- addDatasequenceAnnotations.setEnabled(!referenceAnns.isEmpty());
- addDatasequenceAnnotations.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- addReferenceAnnotations_actionPerformed(referenceAnns);
- }
- });
+ configureReferenceAnnotationsMenu();
sequenceFeature.setText(MessageManager
.getString("label.create_sequence_feature"));
sequenceFeature.addActionListener(new ActionListener()
}
/**
- * Get a list of any annotations on the dataset sequences in the current
- * selection group that are not also on the alignment.
- * <p/>
- * The criteria for 'on the alignment' is finding an annotation that matches
- * on sequenceRef.datasetSequence, calcId and label.
- *
- * @return
+ * Check for any annotations on the underlying dataset sequences (for the
+ * current selection group) which are not on the alignment. If any are found,
+ * enable the option to add them to the alignment. The criteria for 'on the
+ * alignment' is finding an annotation that matches on
+ * sequenceRef.datasetSequence, calcId and label.
*/
- protected List<AlignmentAnnotation> getDatasequenceAnnotationsNotOnAlignment(
- SequenceGroup sg)
+ protected void configureReferenceAnnotationsMenu()
{
- List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
-
- for (SequenceI seq : sg.getSequences())
+ addDatasequenceAnnotations.setText(MessageManager
+ .getString("label.add_reference_annotations"));
+ addDatasequenceAnnotations.setEnabled(false);
+
+ /*
+ * Temporary store so we can write distinct calcId / type pairs on the
+ * tooltip.
+ */
+ Map<String, String> tipEntries = new HashMap<String, String>();
+ StringBuilder tooltip = new StringBuilder(64);
+ tooltip.append(MessageManager.getString("label.add_annotations_for"));
+
+ // this menu option only applies for a Selection
+ if (this.ap.av.getSelectionGroup() == null)
+ {
+ return;
+ }
+
+ /*
+ * For each sequence selected in the alignment, make a list of any
+ * annotations on the underlying dataset sequence which are not already on
+ * the sequence in the alignment.
+ *
+ * Build a map of { alignmentSequence, <List of annotations to add> }
+ */
+ final Map<SequenceI, List<AlignmentAnnotation>> candidates = new HashMap<SequenceI, List<AlignmentAnnotation>>();
+ for (SequenceI seq : this.ap.av.getSelectionGroup().getSequences())
{
SequenceI dataset = seq.getDatasetSequence();
+ if (dataset == null)
+ {
+ continue;
+ }
AlignmentAnnotation[] datasetAnnotations = dataset.getAnnotation();
- if (datasetAnnotations != null)
+ if (datasetAnnotations == null)
+ {
+ continue;
+ }
+ final List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
+ for (AlignmentAnnotation dsann : datasetAnnotations)
{
- for (AlignmentAnnotation dsann : datasetAnnotations)
+ /*
+ * If the sequence has no annotation that matches this one, then add
+ * this one to the results list.
+ */
+ if (seq.getAlignmentAnnotations(dsann.getCalcId(), dsann.label)
+ .isEmpty())
{
- /*
- * If the alignment has no annotation that matches this one...
- */
- if (!ap.getAlignment()
- .findAnnotation(dataset, dsann.getCalcId(), dsann.label)
- .iterator().hasNext())
- {
- /*
- * ...then add it to the result list
- */
- result.add(dsann);
- }
+ result.add(dsann);
+ tipEntries.put(dsann.getCalcId(), dsann.label);
}
}
+ /*
+ * Save any addable annotations for this sequence
+ */
+ if (!result.isEmpty())
+ {
+ candidates.put(seq, result);
+ }
+ }
+ if (!candidates.isEmpty())
+ {
+ /*
+ * Found annotations that could be added. Enable the menu item, and
+ * configure its tooltip and action.
+ */
+ addDatasequenceAnnotations.setEnabled(true);
+ for (String calcId : tipEntries.keySet())
+ {
+ tooltip.append("<br/>" + calcId + "/" + tipEntries.get(calcId));
+ }
+ String tooltipText = JvSwingUtils.wrapTooltip(true,
+ tooltip.toString());
+ addDatasequenceAnnotations.setToolTipText(tooltipText);
+
+ addDatasequenceAnnotations.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ addReferenceAnnotations_actionPerformed(candidates);
+ }
+ });
}
- return result;
}
/**
- * Add any annotations on the sequence dataset to the alignment (that are not
- * already copied to it).
+ * Add annotations to the sequences and to the alignment.
+ *
+ * @param candidates
+ * a map whose keys are sequences on the alignment, and values a list
+ * of annotations to add to each sequence
*/
protected void addReferenceAnnotations_actionPerformed(
- List<AlignmentAnnotation> anns)
+ Map<SequenceI, List<AlignmentAnnotation>> candidates)
{
- for (AlignmentAnnotation ann : anns)
+ for (SequenceI seq : candidates.keySet())
{
- // todo: copy, add, adjust...
+ for (AlignmentAnnotation ann : candidates.get(seq))
+ {
+ AlignmentAnnotation copyAnn = new AlignmentAnnotation(ann);
+ int startRes = 0;
+ int endRes = ann.annotations.length;
+ final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
+ if (selectionGroup != null)
+ {
+ startRes = selectionGroup.getStartRes();
+ endRes = selectionGroup.getEndRes();
+ }
+ copyAnn.restrict(startRes, endRes);
+
+ // add to the sequence (sets correct copyAnn.datasetSequence)
+ seq.addAlignmentAnnotation(copyAnn);
+ // adjust for gaps
+ copyAnn.adjustForAlignment();
+ // add to the alignment and set visible
+ this.ap.getAlignment().addAnnotation(copyAnn);
+ copyAnn.visible = true;
+ }
}
+ refresh();
}
protected void sequenceSelectionDetails_actionPerformed()