From eaf9092bfa74b5162589c8775f68a19dd79dbb1d Mon Sep 17 00:00:00 2001 From: gmungoc Date: Fri, 31 Oct 2014 16:18:33 +0000 Subject: [PATCH] JAL-1152 menu tweaks plus new option to put Autocalc at top or below. --- resources/lang/Messages.properties | 10 +- src/jalview/analysis/AlignmentAnnotationUtils.java | 19 +- src/jalview/analysis/AnnotationSorter.java | 91 +++++++--- src/jalview/bin/Cache.java | 5 +- src/jalview/gui/AlignFrame.java | 41 +++-- src/jalview/gui/AlignViewport.java | 32 ++-- src/jalview/gui/AlignmentPanel.java | 4 +- src/jalview/gui/PopupMenu.java | 7 - src/jalview/jbgui/GAlignFrame.java | 184 +++++++++++++++----- .../analysis/AlignmentAnnotationUtilsTest.java | 52 +++++- test/jalview/analysis/AnnotationSorterTest.java | 26 +-- 11 files changed, 347 insertions(+), 124 deletions(-) diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 176e3e9..fe04105 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -200,8 +200,10 @@ label.average_distance_bloslum62 = Average Distance Using BLOSUM62 label.neighbour_blosum62 = Neighbour Joining Using BLOSUM62 label.show_annotations = Show annotations label.hide_annotations = Hide annotations -label.show_all_annotations = Show all -label.hide_all_annotations = Hide all +label.show_all_seq_annotations = Show sequence related +label.hide_all_seq_annotations = Hide sequence related +label.show_all_al_annotations = Show alignment related +label.hide_all_al_annotations = Hide alignment related label.hide_all = Hide all label.add_reference_annotations = Add reference annotations label.find_tip = Search alignment, selection or sequence ids for a subsequence (ignoring gaps).
Accepts regular expressions - search Help for 'regex' for details. @@ -239,6 +241,8 @@ label.show_consensus_logo = Show Consensus Logo label.norm_consensus_logo = Normalise Consensus Logo label.apply_all_groups = Apply to all groups label.autocalculated_annotation = Autocalculated Annotation +label.show_first = Show first +label.show_last = Show last label.min_colour = Minimum Colour label.max_colour = Maximum Colour label.use_original_colours = Use Original Colours @@ -481,7 +485,7 @@ label.sort_by_score = Sort by Score label.sort_by_density = Sort by Density label.sequence_sort_by_density = Sequence sort by Density label.sort_annotations_by_sequence = Sort by sequence -label.sort_annotations_by_type = Sort by type +label.sort_annotations_by_label = Sort by label label.reveal = Reveal label.hide_columns = Hide Columns label.load_jalview_annotations = Load Jalview Annotations or Features File diff --git a/src/jalview/analysis/AlignmentAnnotationUtils.java b/src/jalview/analysis/AlignmentAnnotationUtils.java index ef8e670..9bdbf73 100644 --- a/src/jalview/analysis/AlignmentAnnotationUtils.java +++ b/src/jalview/analysis/AlignmentAnnotationUtils.java @@ -1,7 +1,6 @@ package jalview.analysis; import jalview.datamodel.AlignmentAnnotation; -import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.renderer.AnnotationRenderer; @@ -130,21 +129,28 @@ public class AlignmentAnnotationUtils } } /* - * finally add the 'composite group labels' to the appropriate lists, - * depending on whether the group is identified as visible or hidden + * Finally add the 'composite group labels' to the appropriate lists, + * depending on whether the group is identified as visible or hidden. Don't + * add the same label more than once (there may be many graph groups that + * generate it). */ for (String calcId : groupLabels.keySet()) { for (int group : groupLabels.get(calcId).keySet()) { final List groupLabel = groupLabels.get(calcId).get(group); + // don't want to duplicate 'same types in different order' + Collections.sort(groupLabel); if (visibleGraphGroups.get(group)) { if (!shownTypes.containsKey(calcId)) { shownTypes.put(calcId, new ArrayList>()); } - shownTypes.get(calcId).add(groupLabel); + if (!shownTypes.get(calcId).contains(groupLabel)) + { + shownTypes.get(calcId).add(groupLabel); + } } else { @@ -152,7 +158,10 @@ public class AlignmentAnnotationUtils { hiddenTypes.put(calcId, new ArrayList>()); } - hiddenTypes.get(calcId).add(groupLabel); + if (!hiddenTypes.get(calcId).contains(groupLabel)) + { + hiddenTypes.get(calcId).add(groupLabel); + } } } } diff --git a/src/jalview/analysis/AnnotationSorter.java b/src/jalview/analysis/AnnotationSorter.java index 289544c..fa9c1a8 100644 --- a/src/jalview/analysis/AnnotationSorter.java +++ b/src/jalview/analysis/AnnotationSorter.java @@ -17,16 +17,27 @@ import java.util.Comparator; public class AnnotationSorter { - public enum SortOrder + public enum SequenceAnnotationOrder { - SEQUENCE_AND_TYPE, TYPE_AND_SEQUENCE + SEQUENCE_AND_LABEL, LABEL_AND_SEQUENCE, NONE } private final AlignmentI alignment; - public AnnotationSorter(AlignmentI alignmentI) + private boolean showAutocalcAbove; + + /** + * Constructor given an alignment and the location (top or bottom) of + * Consensus and similar. + * + * @param alignmentI + * @param showAutocalculatedAbove + */ + public AnnotationSorter(AlignmentI alignmentI, + boolean showAutocalculatedAbove) { this.alignment = alignmentI; + this.showAutocalcAbove = showAutocalculatedAbove; } /** @@ -39,7 +50,7 @@ public class AnnotationSorter *
  • within the same sequence ref, sort by label (non-case-sensitive)
  • * */ - private final Comparator bySequenceAndType = new Comparator() + private final Comparator bySequenceAndLabel = new Comparator() { @Override public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2) @@ -79,7 +90,7 @@ public class AnnotationSorter *
  • within the same label, sort by order of the related sequences
  • * */ - private final Comparator byTypeAndSequence = new Comparator() + private final Comparator byLabelAndSequence = new Comparator() { @Override public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2) @@ -107,39 +118,66 @@ public class AnnotationSorter } /* - * Sort non-sequence-related after sequence-related. + * Sort non-sequence-related before or after sequence-related. */ if (o1.sequenceRef == null) { - return 1; + return showAutocalcAbove ? -1 : 1; } if (o2.sequenceRef == null) { - return -1; + return showAutocalcAbove ? 1 : -1; } int labelOrder = compareLabels(o1, o2); return labelOrder == 0 ? compareSequences(o1, o2) : labelOrder; } }; - private final Comparator DEFAULT_COMPARATOR = bySequenceAndType; + /** + * noSort leaves sort order unchanged, within sequence- and + * non-sequence-related annotations, but may switch the ordering of these + * groups. Note this is guaranteed (at least in Java 7) as Arrays.sort() is + * guaranteed to be 'stable' (not change ordering of equal items). + */ + private Comparator noSort = new Comparator() + { + @Override + public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2) + { + if (o1 != null && o2 != null) + { + if (o1.sequenceRef == null && o2.sequenceRef != null) + { + return showAutocalcAbove ? -1 : 1; + } + if (o1.sequenceRef != null && o2.sequenceRef == null) + { + return showAutocalcAbove ? 1 : -1; + } + } + return 0; + } + }; /** - * Sort by the specified order. + * Sort by the specified ordering of sequence-specific annotations. * * @param alignmentAnnotations * @param order */ public void sort(AlignmentAnnotation[] alignmentAnnotations, - SortOrder order) + SequenceAnnotationOrder order) { - Comparator comparator = getComparator(order); - - if (alignmentAnnotations != null) + if (order != SequenceAnnotationOrder.NONE) { - synchronized (alignmentAnnotations) + Comparator comparator = getComparator(order); + + if (alignmentAnnotations != null) { - Arrays.sort(alignmentAnnotations, comparator); + synchronized (alignmentAnnotations) + { + Arrays.sort(alignmentAnnotations, comparator); + } } } } @@ -151,18 +189,20 @@ public class AnnotationSorter * @return */ private Comparator getComparator( - SortOrder order) + SequenceAnnotationOrder order) { if (order == null) { - return DEFAULT_COMPARATOR; + return noSort; } switch (order) { - case SEQUENCE_AND_TYPE: - return this.bySequenceAndType; - case TYPE_AND_SEQUENCE: - return this.byTypeAndSequence; + case NONE: + return this.noSort; + case SEQUENCE_AND_LABEL: + return this.bySequenceAndLabel; + case LABEL_AND_SEQUENCE: + return this.byLabelAndSequence; default: throw new UnsupportedOperationException(order.toString()); } @@ -216,13 +256,16 @@ public class AnnotationSorter { return 0; } + /* + * Sort non-sequence-related before or after sequence-related. + */ if (seq1 == null) { - return 1; + return showAutocalcAbove ? -1 : 1; } if (seq2 == null) { - return -1; + return showAutocalcAbove ? 1 : -1; } // get sequence index - but note -1 means 'at end' so needs special handling int index1 = AlignmentUtils.getSequenceIndex(alignment, seq1); diff --git a/src/jalview/bin/Cache.java b/src/jalview/bin/Cache.java index 7aab4ae..2a9f53d 100755 --- a/src/jalview/bin/Cache.java +++ b/src/jalview/bin/Cache.java @@ -72,7 +72,10 @@ import org.apache.log4j.SimpleLayout; *
  • SHOW_QUALITY show alignment quality annotation
  • *
  • SHOW_ANNOTATIONS show alignment annotation rows
  • *
  • SHOW_CONSERVATION show alignment conservation annotation
  • - *
  • SORT_ANNOTATIONS currently either SEQUENCE_AND_TYPE or TYPE_AND_SEQUENCE
  • + *
  • SORT_ANNOTATIONS currently either SEQUENCE_AND_LABEL or + * LABEL_AND_SEQUENCE
  • + *
  • SHOW_AUTOCALC_ABOVE true to show autocalculated annotations above + * sequence annotations
  • *
  • CENTRE_COLUMN_LABELS centre the labels at each column in a displayed * annotation row
  • *
  • DEFAULT_COLOUR default colour scheme to apply for a new alignment
  • diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index a50776e..0069818 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -23,7 +23,6 @@ package jalview.gui; import jalview.analysis.AAFrequency; import jalview.analysis.AlignmentSorter; import jalview.analysis.AlignmentUtils; -import jalview.analysis.AnnotationSorter.SortOrder; import jalview.analysis.Conservation; import jalview.analysis.CrossRef; import jalview.analysis.NJTree; @@ -280,7 +279,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } /** - * Make a new AlignFrame from exisiting alignmentPanels + * Make a new AlignFrame from existing alignmentPanels * * @param ap * AlignmentPanel @@ -747,10 +746,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, scaleRight.setVisible(av.wrapAlignment); annotationPanelMenuItem.setState(av.showAnnotation); /* - * Show/hide all annotations only enabled if annotation panel is shown + * Show/hide annotations only enabled if annotation panel is shown */ - showAllAnnotations.setEnabled(annotationPanelMenuItem.getState()); - hideAllAnnotations.setEnabled(annotationPanelMenuItem.getState()); + showAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState()); + hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState()); + showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState()); + hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState()); viewBoxesMenuItem.setSelected(av.showBoxes); viewTextMenuItem.setSelected(av.showText); showNonconservedMenuItem.setSelected(av.getShowUnconserved()); @@ -3101,8 +3102,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, final boolean setVisible = annotationPanelMenuItem.isSelected(); viewport.setShowAnnotation(setVisible); alignPanel.setAnnotationVisible(setVisible); - this.showAllAnnotations.setEnabled(setVisible); - this.hideAllAnnotations.setEnabled(setVisible); + this.showAllSeqAnnotations.setEnabled(setVisible); + this.hideAllSeqAnnotations.setEnabled(setVisible); + this.showAllAlAnnotations.setEnabled(setVisible); + this.hideAllAlAnnotations.setEnabled(setVisible); } @Override @@ -5767,17 +5770,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } /** - * Action on selection of menu option to Show or Hide all annotations. + * Action on selection of menu options to Show or Hide annotations. * - * @param visibile + * @param visible + * @param forSequences + * update sequence-related annotations + * @param forAlignment + * update non-sequence-related annotations */ @Override - protected void setAllAnnotationsVisibility(boolean visible) + protected void setAnnotationsVisibility(boolean visible, + boolean forSequences, boolean forAlignment) { for (AlignmentAnnotation aa : alignPanel.getAlignment() .getAlignmentAnnotation()) { - aa.visible = visible; + boolean apply = (aa.sequenceRef == null && forAlignment) + || (aa.sequenceRef != null && forSequences); + if (apply) + { + aa.visible = visible; + } } this.alignPanel.paintAlignment(true); } @@ -5786,9 +5799,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * Store selected annotation sort order for the view and repaint. */ @Override - protected void sortAnnotations_actionPerformed(SortOrder sortOrder) + protected void sortAnnotations_actionPerformed() { - this.alignPanel.av.setSortAnnotationsBy(sortOrder); + this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder()); + this.alignPanel.av + .setShowAutocalculatedAbove(isShowAutoCalculatedAbove()); alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState()); } } diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 6969513..5c07f6e 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -38,7 +38,7 @@ */ package jalview.gui; -import jalview.analysis.AnnotationSorter.SortOrder; +import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; import jalview.analysis.NJTree; import jalview.api.AlignViewportI; import jalview.bin.Cache; @@ -100,7 +100,7 @@ public class AlignViewport extends AlignmentViewport implements boolean showAnnotation = true; - SortOrder sortAnnotationsBy = null; + SequenceAnnotationOrder sortAnnotationsBy = null; int charHeight; @@ -362,13 +362,13 @@ public class AlignViewport extends AlignmentViewport implements } wrapAlignment = Cache.getDefault("WRAP_ALIGNMENT", false); - showUnconserved = Cache.getDefault("SHOW_UNCONSERVED", - false); + showUnconserved = Cache.getDefault("SHOW_UNCONSERVED", false); sortByTree = Cache.getDefault("SORT_BY_TREE", false); - followSelection = Cache.getDefault("FOLLOW_SELECTIONS", - true); - sortAnnotationsBy = SortOrder.valueOf(Cache.getDefault( - "SORT_ANNOTATIONS", SortOrder.SEQUENCE_AND_TYPE.name())); + followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true); + sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault( + "SORT_ANNOTATIONS", SequenceAnnotationOrder.SEQUENCE_AND_LABEL.name())); + showAutocalculatedAbove = Cache + .getDefault("SHOW_AUTOCALC_ABOVE", false); } /** @@ -1258,6 +1258,8 @@ public class AlignViewport extends AlignmentViewport implements private Hashtable calcIdParams = new Hashtable(); + private boolean showAutocalculatedAbove; + public AutoCalcSetting getCalcIdSettingsFor(String calcId) { return calcIdParams.get(calcId); @@ -1276,13 +1278,23 @@ public class AlignViewport extends AlignmentViewport implements } } - protected SortOrder getSortAnnotationsBy() + protected SequenceAnnotationOrder getSortAnnotationsBy() { return sortAnnotationsBy; } - protected void setSortAnnotationsBy(SortOrder sortAnnotationsBy) + protected void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy) { this.sortAnnotationsBy = sortAnnotationsBy; } + + protected boolean isShowAutocalculatedAbove() + { + return showAutocalculatedAbove; + } + + protected void setShowAutocalculatedAbove(boolean showAutocalculatedAbove) + { + this.showAutocalculatedAbove = showAutocalculatedAbove; + } } diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index dd21819..d610931 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -741,7 +741,9 @@ public class AlignmentPanel extends GAlignmentPanel implements */ public void paintAlignment(boolean updateOverview) { - new AnnotationSorter(getAlignment()).sort(getAlignment() + final AnnotationSorter sorter = new AnnotationSorter(getAlignment(), + av.isShowAutocalculatedAbove()); + sorter.sort(getAlignment() .getAlignmentAnnotation(), av.getSortAnnotationsBy()); repaint(); diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 175adea..d6e370f 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -22,8 +22,6 @@ package jalview.gui; import jalview.analysis.AAFrequency; import jalview.analysis.AlignmentAnnotationUtils; -import jalview.analysis.AnnotationSorter; -import jalview.analysis.AnnotationSorter.SortOrder; import jalview.analysis.Conservation; import jalview.commands.ChangeCaseCommand; import jalview.commands.EditCommand; @@ -1901,11 +1899,6 @@ public class PopupMenu extends JPopupMenu copyAnn.visible = true; } } - // TODO: save annotation sort order on AlignViewport - // do sorting from AlignmentPanel.updateAnnotation() - new AnnotationSorter(this.ap.getAlignment()).sort(this.ap - .getAlignment().getAlignmentAnnotation(), - SortOrder.SEQUENCE_AND_TYPE); refresh(); } diff --git a/src/jalview/jbgui/GAlignFrame.java b/src/jalview/jbgui/GAlignFrame.java index 3591056..dba77dd 100755 --- a/src/jalview/jbgui/GAlignFrame.java +++ b/src/jalview/jbgui/GAlignFrame.java @@ -20,7 +20,7 @@ */ package jalview.jbgui; -import jalview.analysis.AnnotationSorter.SortOrder; +import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; import jalview.bin.Cache; import jalview.gui.JvSwingUtils; import jalview.schemes.ColourSchemeProperty; @@ -307,13 +307,17 @@ public class GAlignFrame extends JInternalFrame JMenuItem showAllhidden = new JMenuItem(); - protected JMenuItem showAllAnnotations = new JMenuItem(); + protected JMenuItem showAllSeqAnnotations = new JMenuItem(); - protected JMenuItem hideAllAnnotations = new JMenuItem(); + protected JMenuItem hideAllSeqAnnotations = new JMenuItem(); + + protected JMenuItem showAllAlAnnotations = new JMenuItem(); + + protected JMenuItem hideAllAlAnnotations = new JMenuItem(); protected JCheckBoxMenuItem sortAnnBySequence = new JCheckBoxMenuItem(); - protected JCheckBoxMenuItem sortAnnByType = new JCheckBoxMenuItem(); + protected JCheckBoxMenuItem sortAnnByLabel = new JCheckBoxMenuItem(); protected JCheckBoxMenuItem hiddenMarkers = new JCheckBoxMenuItem(); @@ -367,8 +371,16 @@ public class GAlignFrame extends JInternalFrame protected JCheckBoxMenuItem applyAutoAnnotationSettings = new JCheckBoxMenuItem(); + protected JCheckBoxMenuItem showAutoFirst = new JCheckBoxMenuItem(); + + protected JCheckBoxMenuItem showAutoLast = new JCheckBoxMenuItem(); + private JMenuItem grpsFromSelection = new JMenuItem(); + private SequenceAnnotationOrder annotationSortOrder; + + private boolean showAutoCalculatedAbove = false; + public GAlignFrame() { try @@ -1081,29 +1093,50 @@ public class GAlignFrame extends JInternalFrame annotationPanelMenuItem_actionPerformed(e); } }); - /* - * Show/hide all annotations only enabled if annotation panel is shown - */ - showAllAnnotations.setText(MessageManager - .getString("label.show_all_annotations")); - showAllAnnotations.setEnabled(annotationPanelMenuItem.getState()); - showAllAnnotations.addActionListener(new ActionListener() + showAllAlAnnotations.setText(MessageManager + .getString("label.show_all_al_annotations")); + final boolean isAnnotationPanelShown = annotationPanelMenuItem + .getState(); + showAllAlAnnotations.setEnabled(isAnnotationPanelShown); + showAllAlAnnotations.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showAllAnnotations_actionPerformed(false, true); + } + }); + hideAllAlAnnotations.setText(MessageManager + .getString("label.hide_all_al_annotations")); + hideAllAlAnnotations.setEnabled(isAnnotationPanelShown); + hideAllAlAnnotations.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + hideAllAnnotations_actionPerformed(false, true); + } + }); + showAllSeqAnnotations.setText(MessageManager + .getString("label.show_all_seq_annotations")); + showAllSeqAnnotations.setEnabled(isAnnotationPanelShown); + showAllSeqAnnotations.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - showAllAnnotations_actionPerformed(); + showAllAnnotations_actionPerformed(true, false); } }); - hideAllAnnotations.setText(MessageManager - .getString("label.hide_all_annotations")); - hideAllAnnotations.setEnabled(annotationPanelMenuItem.getState()); - hideAllAnnotations.addActionListener(new ActionListener() + hideAllSeqAnnotations.setText(MessageManager + .getString("label.hide_all_seq_annotations")); + hideAllSeqAnnotations.setEnabled(isAnnotationPanelShown); + hideAllSeqAnnotations.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - hideAllAnnotations_actionPerformed(); + hideAllAnnotations_actionPerformed(true, false); } }); sortAnnBySequence.setText(MessageManager @@ -1113,25 +1146,25 @@ public class GAlignFrame extends JInternalFrame @Override public void actionPerformed(ActionEvent e) { - sortAnnBySequence.setEnabled(false); - sortAnnBySequence.setState(true); - sortAnnByType.setEnabled(true); - sortAnnByType.setState(false); - sortAnnotations_actionPerformed(SortOrder.SEQUENCE_AND_TYPE); + boolean newState = sortAnnBySequence.getState(); + sortAnnByLabel.setState(false); + setAnnotationSortOrder(newState ? SequenceAnnotationOrder.SEQUENCE_AND_LABEL + : SequenceAnnotationOrder.NONE); + sortAnnotations_actionPerformed(); } }); - sortAnnByType.setText(MessageManager - .getString("label.sort_annotations_by_type")); - sortAnnByType.addActionListener(new ActionListener() + sortAnnByLabel.setText(MessageManager + .getString("label.sort_annotations_by_label")); + sortAnnByLabel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - sortAnnByType.setEnabled(false); - sortAnnByType.setState(true); - sortAnnBySequence.setEnabled(true); + boolean newState = sortAnnByLabel.getState(); sortAnnBySequence.setState(false); - sortAnnotations_actionPerformed(SortOrder.TYPE_AND_SEQUENCE); + setAnnotationSortOrder(newState ? SequenceAnnotationOrder.LABEL_AND_SEQUENCE + : SequenceAnnotationOrder.NONE); + sortAnnotations_actionPerformed(); } }); colourTextMenuItem.setText(MessageManager @@ -1374,13 +1407,38 @@ public class GAlignFrame extends JInternalFrame applyAutoAnnotationSettings.setVisible(true); applyAutoAnnotationSettings.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { applyAutoAnnotationSettings_actionPerformed(e); } + }); + showAutoFirst.setText(MessageManager.getString("label.show_first")); + showAutoFirst.setState(Cache.getDefault("SHOW_AUTOCALC_ABOVE", false)); + showAutoFirst.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + boolean sortFirst = showAutoFirst.getState(); + setShowAutoCalculatedAbove(sortFirst); + showAutoLast.setState(!sortFirst); + sortAnnotations_actionPerformed(); + } + }); + showAutoLast.setText(MessageManager.getString("label.show_last")); + showAutoLast.setState(!showAutoFirst.getState()); + showAutoLast.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + boolean sortLast = showAutoLast.getState(); + setShowAutoCalculatedAbove(!sortLast); + showAutoFirst.setState(!sortLast); + sortAnnotations_actionPerformed(); + } }); nucleotideColour.setText(MessageManager.getString("label.nucleotide")); @@ -2188,10 +2246,18 @@ public class GAlignFrame extends JInternalFrame viewMenu.addSeparator(); viewMenu.add(followHighlightMenuItem); annotationsMenu.add(annotationPanelMenuItem); - annotationsMenu.add(showAllAnnotations); - annotationsMenu.add(hideAllAnnotations); + annotationsMenu.addSeparator(); + annotationsMenu.add(showAllAlAnnotations); + annotationsMenu.add(hideAllAlAnnotations); + annotationsMenu.addSeparator(); + annotationsMenu.add(showAllSeqAnnotations); + annotationsMenu.add(hideAllSeqAnnotations); annotationsMenu.add(sortAnnBySequence); - annotationsMenu.add(sortAnnByType); + annotationsMenu.add(sortAnnByLabel); + annotationsMenu.addSeparator(); + autoAnnMenu.add(showAutoFirst); + autoAnnMenu.add(showAutoLast); + autoAnnMenu.addSeparator(); autoAnnMenu.add(applyAutoAnnotationSettings); autoAnnMenu.add(showConsensusHistogram); autoAnnMenu.add(showSequenceLogo); @@ -2315,32 +2381,50 @@ public class GAlignFrame extends JInternalFrame * * @param sortOrder */ - protected void sortAnnotations_actionPerformed(SortOrder sortOrder) + protected void sortAnnotations_actionPerformed() { } /** * Action on clicking Show all annotations. + * + * @param forSequences + * update sequence-related annotations + * @param forAlignment + * update non-sequence-related annotations */ - protected void showAllAnnotations_actionPerformed() + protected void showAllAnnotations_actionPerformed(boolean forSequences, + boolean forAlignment) { - setAllAnnotationsVisibility(true); + setAnnotationsVisibility(true, forSequences, forAlignment); } /** * Action on clicking Hide all annotations. + * + * @param forSequences + * update sequence-related annotations + * @param forAlignment + * update non-sequence-related annotations */ - protected void hideAllAnnotations_actionPerformed() + protected void hideAllAnnotations_actionPerformed(boolean forSequences, + boolean forAlignment) { - setAllAnnotationsVisibility(false); + setAnnotationsVisibility(false, forSequences, forAlignment); } /** - * Set the visibility of all annotations to true or false. + * Set the visibility of annotations to true or false. Can act on + * sequence-related annotations, or alignment-related, or both. * * @param visible + * @param forSequences + * update sequence-related annotations + * @param forAlignment + * update non-sequence-related annotations */ - protected void setAllAnnotationsVisibility(boolean visible) + protected void setAnnotationsVisibility(boolean visible, + boolean forSequences, boolean forAlignment) { } @@ -2989,4 +3073,24 @@ public class GAlignFrame extends JInternalFrame // TODO Auto-generated method stub } + + protected boolean isShowAutoCalculatedAbove() + { + return showAutoCalculatedAbove; + } + + protected void setShowAutoCalculatedAbove(boolean showAutoCalculatedAbove) + { + this.showAutoCalculatedAbove = showAutoCalculatedAbove; + } + + protected SequenceAnnotationOrder getAnnotationSortOrder() + { + return annotationSortOrder; + } + + protected void setAnnotationSortOrder(SequenceAnnotationOrder annotationSortOrder) + { + this.annotationSortOrder = annotationSortOrder; + } } diff --git a/test/jalview/analysis/AlignmentAnnotationUtilsTest.java b/test/jalview/analysis/AlignmentAnnotationUtilsTest.java index 1da1939..19a5163 100644 --- a/test/jalview/analysis/AlignmentAnnotationUtilsTest.java +++ b/test/jalview/analysis/AlignmentAnnotationUtilsTest.java @@ -36,7 +36,7 @@ public class AlignmentAnnotationUtilsTest "TIETHKEEELTA-" + EOL; // @formatter:on - private static final int SEQ_ANN_COUNT = 10; + private static final int SEQ_ANN_COUNT = 12; private AlignmentI alignment; @@ -212,6 +212,7 @@ public class AlignmentAnnotationUtilsTest @Test public void testGetShownHiddenTypes_withGraphGroups() { + final int GROUP_3 = 3; final int GROUP_4 = 4; final int GROUP_5 = 5; final int GROUP_6 = 6; @@ -222,10 +223,10 @@ public class AlignmentAnnotationUtilsTest SequenceI[] seqs = alignment.getSequencesArray(); /* - * Configure annotation properties for test + * Annotations for selection group and graph group + * + * Hidden annotations Label2, Label3, in (hidden) group 5 */ - // annotations for selection group and graph group - // hidden annotations Label2, Label3, in (hidden) group 5 anns[2].sequenceRef = seqs[3]; anns[2].visible = false; anns[2].graph = AlignmentAnnotation.LINE_GRAPH; @@ -236,6 +237,19 @@ public class AlignmentAnnotationUtilsTest anns[3].graphGroup = GROUP_5; // need to ensure annotations have the same calcId as well anns[3].setCalcId("CalcId2"); + // annotations for a different hidden group generating the same group label + anns[10].sequenceRef = seqs[0]; + anns[10].visible = false; + anns[10].graph = AlignmentAnnotation.LINE_GRAPH; + anns[10].graphGroup = GROUP_3; + anns[10].label = "Label3"; + anns[10].setCalcId("CalcId2"); + anns[11].sequenceRef = seqs[3]; + anns[11].visible = false; + anns[11].graph = AlignmentAnnotation.LINE_GRAPH; + anns[11].graphGroup = GROUP_3; + anns[11].label = "Label2"; + anns[11].setCalcId("CalcId2"); // annotations Label1 (hidden), Label5 (visible) in group 6 (visible) anns[1].sequenceRef = seqs[3]; @@ -248,9 +262,29 @@ public class AlignmentAnnotationUtilsTest anns[5].graph = AlignmentAnnotation.LINE_GRAPH; anns[5].graphGroup = GROUP_6; anns[5].setCalcId("CalcId1"); + /* + * Annotations 0 and 4 are visible, for a different CalcId and graph group. + * They produce the same label as annotations 1 and 5, which should not be + * duplicated in the results. This case corresponds to (e.g.) many + * occurrences of an IUPred Short/Long annotation group, one per sequence. + */ + anns[4].sequenceRef = seqs[0]; + anns[4].visible = false; + anns[4].graph = AlignmentAnnotation.LINE_GRAPH; + anns[4].graphGroup = GROUP_4; + anns[4].label = "Label1"; + anns[4].setCalcId("CalcId1"); + anns[0].sequenceRef = seqs[0]; + anns[0].visible = true; + anns[0].graph = AlignmentAnnotation.LINE_GRAPH; + anns[0].graphGroup = GROUP_4; + anns[0].label = "Label5"; + anns[0].setCalcId("CalcId1"); - // annotations outwith selection group - should be ignored - // hidden grouped annotations + /* + * Annotations outwith selection group - should be ignored. + */ + // Hidden grouped annotations anns[6].sequenceRef = seqs[2]; anns[6].visible = false; anns[6].graph = AlignmentAnnotation.LINE_GRAPH; @@ -259,6 +293,7 @@ public class AlignmentAnnotationUtilsTest anns[8].visible = false; anns[8].graph = AlignmentAnnotation.LINE_GRAPH; anns[8].graphGroup = GROUP_4; + // visible grouped annotations Label7, Label9 anns[7].sequenceRef = seqs[2]; anns[7].visible = true; @@ -276,13 +311,16 @@ public class AlignmentAnnotationUtilsTest consoleDebug(shownTypes, hiddenTypes); - // CalcId1 / Label1, Label5 (only) should be 'shown', as a compound type + // CalcId1 / Label1, Label5 (only) should be 'shown', once, as a compound + // type + assertEquals(1, shownTypes.size()); assertEquals(1, shownTypes.get("CalcId1").size()); assertEquals(2, shownTypes.get("CalcId1").get(0).size()); assertEquals("Label1", shownTypes.get("CalcId1").get(0).get(0)); assertEquals("Label5", shownTypes.get("CalcId1").get(0).get(1)); // CalcId2 / Label2, Label3 (only) should be 'hidden' + assertEquals(1, hiddenTypes.size()); assertEquals(1, hiddenTypes.get("CalcId2").size()); assertEquals(2, hiddenTypes.get("CalcId2").get(0).size()); assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0)); diff --git a/test/jalview/analysis/AnnotationSorterTest.java b/test/jalview/analysis/AnnotationSorterTest.java index 97dabbc..55dcf6d 100644 --- a/test/jalview/analysis/AnnotationSorterTest.java +++ b/test/jalview/analysis/AnnotationSorterTest.java @@ -2,7 +2,7 @@ package jalview.analysis; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import jalview.analysis.AnnotationSorter.SortOrder; +import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Sequence; @@ -97,8 +97,8 @@ public class AnnotationSorterTest anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP"; // @formatter:on - AnnotationSorter testee = new AnnotationSorter(al); - testee.sort(anns, SortOrder.SEQUENCE_AND_TYPE); + AnnotationSorter testee = new AnnotationSorter(al, false); + testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); assertEquals("label5", anns[0].label); // for sequence 0 assertEquals("label0", anns[1].label); // for sequence 1 assertEquals("iron", anns[2].label); // sequence 3 /iron @@ -133,8 +133,8 @@ public class AnnotationSorterTest anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure"; // @formatter:on - AnnotationSorter testee = new AnnotationSorter(al); - testee.sort(anns, SortOrder.TYPE_AND_SEQUENCE); + AnnotationSorter testee = new AnnotationSorter(al, false); + testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); assertEquals("IRON", anns[0].label); // IRON / sequence 0 assertEquals("iron", anns[1].label); // iron / sequence 3 assertEquals("label0", anns[2].label); // label0 / sequence 1 @@ -165,8 +165,8 @@ public class AnnotationSorterTest anns[i].label = "label" + i; } long startTime = System.currentTimeMillis(); - AnnotationSorter testee = new AnnotationSorter(al); - testee.sort(anns, SortOrder.TYPE_AND_SEQUENCE); + AnnotationSorter testee = new AnnotationSorter(al, false); + testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); long endTime = System.currentTimeMillis(); final long elapsed = endTime - startTime; System.out.println("Timing test for presorted " + numSeqs @@ -199,8 +199,8 @@ public class AnnotationSorterTest anns[i].label = "label" + i; } long startTime = System.currentTimeMillis(); - AnnotationSorter testee = new AnnotationSorter(al); - testee.sort(anns, SortOrder.SEQUENCE_AND_TYPE); + AnnotationSorter testee = new AnnotationSorter(al, false); + testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); long endTime = System.currentTimeMillis(); final long elapsed = endTime - startTime; System.out.println("Timing test for unsorted " + numSeqs @@ -234,8 +234,8 @@ public class AnnotationSorterTest anns[i].label = labels[r.nextInt(labels.length)]; } long startTime = System.currentTimeMillis(); - AnnotationSorter testee = new AnnotationSorter(al); - testee.sort(anns, SortOrder.TYPE_AND_SEQUENCE); + AnnotationSorter testee = new AnnotationSorter(al, false); + testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); long endTime = System.currentTimeMillis(); long elapsed = endTime - startTime; System.out.println("Sort by type for semisorted " + numSeqs @@ -244,7 +244,7 @@ public class AnnotationSorterTest // now resort by sequence startTime = System.currentTimeMillis(); - testee.sort(anns, SortOrder.SEQUENCE_AND_TYPE); + testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); endTime = System.currentTimeMillis(); elapsed = endTime - startTime; System.out.println("Resort by sequence for semisorted " + numSeqs @@ -253,7 +253,7 @@ public class AnnotationSorterTest // now resort by type startTime = System.currentTimeMillis(); - testee.sort(anns, SortOrder.TYPE_AND_SEQUENCE); + testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); endTime = System.currentTimeMillis(); elapsed = endTime - startTime; System.out.println("Resort by type for semisorted " + numSeqs -- 1.7.10.2