JAL-1152 menu tweaks plus new option to put Autocalc at top or below.
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 31 Oct 2014 16:18:33 +0000 (16:18 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 31 Oct 2014 16:18:33 +0000 (16:18 +0000)
resources/lang/Messages.properties
src/jalview/analysis/AlignmentAnnotationUtils.java
src/jalview/analysis/AnnotationSorter.java
src/jalview/bin/Cache.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/jbgui/GAlignFrame.java
test/jalview/analysis/AlignmentAnnotationUtilsTest.java
test/jalview/analysis/AnnotationSorterTest.java

index 176e3e9..fe04105 100644 (file)
@@ -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).<br>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
index ef8e670..9bdbf73 100644 (file)
@@ -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<String> 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<List<String>>());
           }
-          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<List<String>>());
           }
-          hiddenTypes.get(calcId).add(groupLabel);
+          if (!hiddenTypes.get(calcId).contains(groupLabel))
+          {
+            hiddenTypes.get(calcId).add(groupLabel);
+          }
         }
       }
     }
index 289544c..fa9c1a8 100644 (file)
@@ -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
    * <li>within the same sequence ref, sort by label (non-case-sensitive)</li>
    * </ul>
    */
-  private final Comparator<? super AlignmentAnnotation> bySequenceAndType = new Comparator<AlignmentAnnotation>()
+  private final Comparator<? super AlignmentAnnotation> bySequenceAndLabel = new Comparator<AlignmentAnnotation>()
   {
     @Override
     public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
@@ -79,7 +90,7 @@ public class AnnotationSorter
    * <li>within the same label, sort by order of the related sequences</li>
    * </ul>
    */
-  private final Comparator<? super AlignmentAnnotation> byTypeAndSequence = new Comparator<AlignmentAnnotation>()
+  private final Comparator<? super AlignmentAnnotation> byLabelAndSequence = new Comparator<AlignmentAnnotation>()
   {
     @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<? super AlignmentAnnotation> 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<? super AlignmentAnnotation> noSort = new Comparator<AlignmentAnnotation>()
+  {
+    @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<? super AlignmentAnnotation> comparator = getComparator(order);
-
-    if (alignmentAnnotations != null)
+    if (order != SequenceAnnotationOrder.NONE)
     {
-      synchronized (alignmentAnnotations)
+      Comparator<? super AlignmentAnnotation> 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<? super AlignmentAnnotation> 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);
index 7aab4ae..2a9f53d 100755 (executable)
@@ -72,7 +72,10 @@ import org.apache.log4j.SimpleLayout;
  * <li>SHOW_QUALITY show alignment quality annotation</li>
  * <li>SHOW_ANNOTATIONS show alignment annotation rows</li>
  * <li>SHOW_CONSERVATION show alignment conservation annotation</li>
- * <li>SORT_ANNOTATIONS currently either SEQUENCE_AND_TYPE or TYPE_AND_SEQUENCE</li>
+ * <li>SORT_ANNOTATIONS currently either SEQUENCE_AND_LABEL or
+ * LABEL_AND_SEQUENCE</li>
+ * <li>SHOW_AUTOCALC_ABOVE true to show autocalculated annotations above
+ * sequence annotations</li>
  * <li>CENTRE_COLUMN_LABELS centre the labels at each column in a displayed
  * annotation row</li>
  * <li>DEFAULT_COLOUR default colour scheme to apply for a new alignment</li>
index a50776e..0069818 100644 (file)
@@ -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());
   }
 }
index 6969513..5c07f6e 100644 (file)
@@ -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<String, AutoCalcSetting> calcIdParams = new Hashtable<String, AutoCalcSetting>();
 
+  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;
+  }
 }
index dd21819..d610931 100644 (file)
@@ -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();
index 175adea..d6e370f 100644 (file)
@@ -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();
   }
 
index 3591056..dba77dd 100755 (executable)
@@ -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;
+  }
 }
index 1da1939..19a5163 100644 (file)
@@ -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));
index 97dabbc..55dcf6d 100644 (file)
@@ -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